library(swimplot) library(grid) library(gtable) library(readr) library(mosaic) library(dplyr) library(survival) library(survminer) library(ggplot2) library(scales) library(coxphf) library(ggthemes) library(tidyverse) library(gtsummary) library(flextable) library(parameters) library(car) library(ComplexHeatmap) library(tidyverse) library(readxl) library(janitor) library(DT) library(pROC) library(rms)

#ctDNA Detection Rates by Window and Stages

#ctDNA at MRD by Stage
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
circ_data <- subset(circ_data, ctDNA.MRD %in% c("NEGATIVE", "POSITIVE"))
circ_data$Stage <- factor(circ_data$Stage, levels=c("0/I","II","III"))
positive_counts_by_stage <- aggregate(circ_data$ctDNA.MRD == "POSITIVE", by=list(circ_data$Stage), FUN=sum)
total_counts_by_stage <- aggregate(circ_data$ctDNA.MRD, by=list(circ_data$Stage), FUN=length)
combined_data <- data.frame(
  Stage = total_counts_by_stage$Group.1,
  Total_Count = total_counts_by_stage$x,
  Positive_Count = positive_counts_by_stage$x,
  Rate = (positive_counts_by_stage$x / total_counts_by_stage$x) * 100  # Convert to percentage
)
combined_data$Rate <- sprintf("%.2f%%", combined_data$Rate)
overall_total_count <- nrow(circ_data)
overall_positive_count <- nrow(circ_data[circ_data$ctDNA.MRD == "POSITIVE",])
overall_positivity_rate <- (overall_positive_count / overall_total_count) * 100  # Convert to percentage
overall_row <- data.frame(
  Stage = "Overall",
  Total_Count = overall_total_count,
  Positive_Count = overall_positive_count,
  Rate = sprintf("%.2f%%", overall_positivity_rate)
)
combined_data <- rbind(combined_data, overall_row)
print(combined_data)
    Stage Total_Count Positive_Count   Rate
1     0/I           6              2 33.33%
2      II           6              4 66.67%
3     III          16              9 56.25%
4 Overall          28             15 53.57%
#ctDNA at MRD by NAC status
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
circ_data <- subset(circ_data, ctDNA.MRD %in% c("NEGATIVE", "POSITIVE"))
circ_data$NAC <- factor(circ_data$NAC, levels=c("FALSE","TRUE"))
positive_counts_by_stage <- aggregate(circ_data$ctDNA.MRD == "POSITIVE", by=list(circ_data$NAC), FUN=sum)
total_counts_by_stage <- aggregate(circ_data$ctDNA.MRD, by=list(circ_data$NAC), FUN=length)
combined_data <- data.frame(
  Stage = total_counts_by_stage$Group.1,
  Total_Count = total_counts_by_stage$x,
  Positive_Count = positive_counts_by_stage$x,
  Rate = (positive_counts_by_stage$x / total_counts_by_stage$x) * 100  # Convert to percentage
)
combined_data$Rate <- sprintf("%.2f%%", combined_data$Rate)
overall_total_count <- nrow(circ_data)
overall_positive_count <- nrow(circ_data[circ_data$ctDNA.MRD == "POSITIVE",])
overall_positivity_rate <- (overall_positive_count / overall_total_count) * 100  # Convert to percentage
overall_row <- data.frame(
  Stage = "Overall",
  Total_Count = overall_total_count,
  Positive_Count = overall_positive_count,
  Rate = sprintf("%.2f%%", overall_positivity_rate)
)
combined_data <- rbind(combined_data, overall_row)
print(combined_data)
    Stage Total_Count Positive_Count   Rate
1   FALSE          23             12 52.17%
2    TRUE           5              3 60.00%
3 Overall          28             15 53.57%
#ctDNA at Surveillance by Stage
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
circ_data$Stage <- factor(circ_data$Stage, levels=c("0/I","II","III"))
positive_counts_by_stage <- aggregate(circ_data$ctDNA.Surveillance == "POSITIVE", by=list(circ_data$Stage), FUN=sum)
total_counts_by_stage <- aggregate(circ_data$ctDNA.Surveillance, by=list(circ_data$Stage), FUN=length)
combined_data <- data.frame(
  Stage = total_counts_by_stage$Group.1,
  Total_Count = total_counts_by_stage$x,
  Positive_Count = positive_counts_by_stage$x,
  Rate = (positive_counts_by_stage$x / total_counts_by_stage$x) * 100  # Convert to percentage
)
combined_data$Rate <- sprintf("%.2f%%", combined_data$Rate)
overall_total_count <- nrow(circ_data)
overall_positive_count <- nrow(circ_data[circ_data$ctDNA.Surveillance == "POSITIVE",])
overall_positivity_rate <- (overall_positive_count / overall_total_count) * 100  # Convert to percentage
overall_row <- data.frame(
  Stage = "Overall",
  Total_Count = overall_total_count,
  Positive_Count = overall_positive_count,
  Rate = sprintf("%.2f%%", overall_positivity_rate)
)
combined_data <- rbind(combined_data, overall_row)
print(combined_data)
    Stage Total_Count Positive_Count   Rate
1     0/I           9              3 33.33%
2      II           9              7 77.78%
3     III          22             12 54.55%
4 Overall          40             22 55.00%
#ctDNA at anytime by Stage
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.anytime!="",]
circ_data$ctDNA.anytime <- factor(circ_data$ctDNA.anytime, levels=c("NEGATIVE","POSITIVE"))
circ_data$Stage <- factor(circ_data$Stage, levels=c("0/I","II","III"))
positive_counts_by_stage <- aggregate(circ_data$ctDNA.anytime == "POSITIVE", by=list(circ_data$Stage), FUN=sum)
total_counts_by_stage <- aggregate(circ_data$ctDNA.anytime, by=list(circ_data$Stage), FUN=length)
combined_data <- data.frame(
  Stage = total_counts_by_stage$Group.1,
  Total_Count = total_counts_by_stage$x,
  Positive_Count = positive_counts_by_stage$x,
  Rate = (positive_counts_by_stage$x / total_counts_by_stage$x) * 100  # Convert to percentage
)
combined_data$Rate <- sprintf("%.2f%%", combined_data$Rate)
overall_total_count <- nrow(circ_data)
overall_positive_count <- nrow(circ_data[circ_data$ctDNA.anytime == "POSITIVE",])
overall_positivity_rate <- (overall_positive_count / overall_total_count) * 100  # Convert to percentage
overall_row <- data.frame(
  Stage = "Overall",
  Total_Count = overall_total_count,
  Positive_Count = overall_positive_count,
  Rate = sprintf("%.2f%%", overall_positivity_rate)
)
combined_data <- rbind(combined_data, overall_row)
print(combined_data)
    Stage Total_Count Positive_Count   Rate
1     0/I           9              3 33.33%
2      II          10              8 80.00%
3     III          28             22 78.57%
4 Overall          47             33 70.21%

#Demographics Table

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]

circ_data_subset <- circ_data %>%
  select(
    Age,
    Sex,
    Stage,
    NAC,
    ACT,
    ACT.type,
    RFS.Event,
    OS.Event,
    OS.months) %>%
  mutate(
    Age = as.numeric(Age),
    Sex = factor(Sex),
    Stage = factor(Stage),
    NAC = factor(NAC),
    ACT = factor(ACT),
    ACT.type = factor(ACT.type),
    RFS.Event = factor(RFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Recurrence", "Recurrence")),
    OS.Event = factor(OS.Event, levels = c("FALSE", "TRUE"), labels = c("Alive", "Deceased")),
    OS.months = as.numeric(OS.months)) 
table1 <- circ_data_subset %>%
  tbl_summary(
    statistic = list(
      all_continuous() ~ "{median} ({min} - {max})",
      all_categorical() ~ "{n} ({p}%)")) %>%
  bold_labels()
table1
Characteristic N = 471
Age 77 (47 - 92)
Sex
    Female 17 (36%)
    Male 30 (64%)
Stage
    0/I 9 (19%)
    II 10 (21%)
    III 28 (60%)
NAC
    FALSE 40 (85%)
    TRUE 7 (15%)
ACT
    FALSE 13 (28%)
    TRUE 34 (72%)
ACT.type
     13 (28%)
    Chemotherapy 18 (38%)
    Immunotherapy 16 (34%)
RFS.Event
    No Recurrence 23 (49%)
    Recurrence 24 (51%)
OS.Event
    Alive 41 (87%)
    Deceased 6 (13%)
OS.months 21 (3 - 48)
1 Median (Min - Max); n (%)
fit1 <- as_flex_table(
  table1,
  include = everything(),
  return_calls = FALSE)
fit1

Characteristic

N = 471

Age

77 (47 - 92)

Sex

Female

17 (36%)

Male

30 (64%)

Stage

0/I

9 (19%)

II

10 (21%)

III

28 (60%)

NAC

FALSE

40 (85%)

TRUE

7 (15%)

ACT

FALSE

13 (28%)

TRUE

34 (72%)

ACT.type

13 (28%)

Chemotherapy

18 (38%)

Immunotherapy

16 (34%)

RFS.Event

No Recurrence

23 (49%)

Recurrence

24 (51%)

OS.Event

Alive

41 (87%)

Deceased

6 (13%)

OS.months

21 (3 - 48)

1Median (Min - Max); n (%)

save_as_docx(fit1, path= "~/Downloads/1. Demographics Table_RWE UTUC.docx")

#Overview plot

rm(list=ls())
setwd("~/Downloads") 
clinstage<- read.csv("RWE UTUC_OP 102025.csv")
clinstage <- clinstage[clinstage$Final.cohort=="TRUE",]
clinstage_df<- as.data.frame(clinstage)

#Display the swimmer plot with the label box
oplot<-swimmer_plot(df=clinstage_df,
                    id='PatientName',
                    end='fu.diff.months',
                    fill='gray',
                    width=.01,)
oplot <- oplot + theme(panel.border = element_blank())
oplot <- oplot + scale_y_continuous(breaks = seq(0, 96, by = 3))
oplot <- oplot + labs(x ="Patients" , y="Time from Surgery (Months)")
oplot



##plot events
oplot_ev1 <- oplot + swimmer_points(df_points=clinstage_df,
                                    id='PatientName',
                                    time='date.diff.months',
                                    name_shape ='Event_type',
                                    name_col = 'Event',
                                    size=3.5,fill='black',
                                    #col='darkgreen'
)
oplot_ev1


#Shape customization to Event_type

oplot_ev1.1 <- oplot_ev1 + ggplot2::scale_shape_manual(name="Event_type",values=c(1,16,6,18,4),breaks=c('ctDNA_neg','ctDNA_pos','Imaging','Surgery','Death'))
oplot_ev1.1


#plot treatment

oplot_ev2 <- oplot_ev1.1 + swimmer_lines(df_lines=clinstage_df,
                                         id='PatientName',
                                         start='Tx_start.months',
                                         end='Tx_end.months',
                                         name_col='Tx_type',
                                         size=3.5,
                                         name_alpha = 1.0)
oplot_ev2 <- oplot_ev2 + guides(linetype = guide_legend(override.aes = list(size = 5, color = "black")))
oplot_ev2  


#colour customization
oplot_ev2.2 <- oplot_ev2 + ggplot2::scale_color_manual(name="Event",values=c( "lightblue","purple","green", "black", "black", "black", "lightblue","lightblue","green","red","green","green","blue","lightblue", "blue", "blue"))
oplot_ev2.2

#RFS by ctDNA status at MRD Window

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.MRD!="",]
circ_data$RFS.MRD <- circ_data$RFS - circ_data$ctDNA.timing
circ_data$RFS.MRD.months <- circ_data$RFS.MRD / 30.437

survfit(Surv(time = circ_data$RFS.MRD.months, event = circ_data$RFS.Event)~ctDNA.MRD, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.MRD.months, event = circ_data$RFS.Event) ~ 
    ctDNA.MRD, data = circ_data)

                    n events median 0.95LCL 0.95UCL
ctDNA.MRD=NEGATIVE 13      3     NA   12.62      NA
ctDNA.MRD=POSITIVE 15     11   9.07    6.77      NA
event_summary <- circ_data %>%
  group_by(ctDNA.MRD) %>%
  summarise(
    Total = n(),
    Events = sum(RFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.MRD Total Events Fraction Percentage
  <chr>     <int>  <int>    <dbl>      <dbl>
1 NEGATIVE     13      3    0.231       23.1
2 POSITIVE     15     11    0.733       73.3
surv_object <-Surv(time = circ_data$RFS.MRD.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.MRD, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=3, palette=c("blue","red"), title="RFS - ctDNA at the MRD Window", ylab= "Recurrence-Free Survival", xlab="Time from Landmark Time point (months)", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 18))
Call: survfit(formula = surv_object ~ ctDNA.MRD, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.MRD=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     13       0    1.000   0.000        1.000        1.000
   12      8       2    0.818   0.116        0.447        0.951
   18      5       1    0.716   0.140        0.350        0.899

                ctDNA.MRD=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     15       0    1.000   0.000        1.000        1.000
   12      6       8    0.467   0.129        0.212        0.687
   18      3       2    0.311   0.124        0.102        0.550
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.MRD, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.MRD, data = circ_data)

  n= 28, number of events= 14 

                    coef exp(coef) se(coef)     z Pr(>|z|)  
ctDNA.MRDPOSITIVE 1.4752    4.3720   0.6539 2.256   0.0241 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                  exp(coef) exp(-coef) lower .95 upper .95
ctDNA.MRDPOSITIVE     4.372     0.2287     1.214     15.75

Concordance= 0.677  (se = 0.057 )
Likelihood ratio test= 6.31  on 1 df,   p=0.01
Wald test            = 5.09  on 1 df,   p=0.02
Score (logrank) test = 6.05  on 1 df,   p=0.01
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 4.37 (1.21-15.75); p = 0.024"
circ_data$ctDNA.MRD <- factor(circ_data$ctDNA.MRD, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$RFS.Event <- factor(circ_data$RFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Recurrence", "Recurrence"))
contingency_table <- table(circ_data$ctDNA.MRD, circ_data$RFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 5.1692, df = 1, p-value = 0.02299
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 0.0213
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
  1.28780 74.44434
sample estimates:
odds ratio 
  8.328277 
print(contingency_table)
          
           No Recurrence Recurrence
  Negative            10          3
  Positive             4         11
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at MRD", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Recurrence",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Recurrence" = "blue", "Recurrence" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Recurrence label size

#RFS by ctDNA status at Surveillance Window

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]

survfit(Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)~ctDNA.Surveillance, data = circ_data)
Call: survfit(formula = Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) ~ 
    ctDNA.Surveillance, data = circ_data)

                             n events median 0.95LCL 0.95UCL
ctDNA.Surveillance=NEGATIVE 18      3     NA      NA      NA
ctDNA.Surveillance=POSITIVE 22     17   13.9    10.1      NA
event_summary <- circ_data %>%
  group_by(ctDNA.Surveillance) %>%
  summarise(
    Total = n(),
    Events = sum(RFS.Event),
    Fraction = Events / n(),
    Percentage = (Events / n()) * 100
  )
print(event_summary)
# A tibble: 2 × 5
  ctDNA.Surveillance Total Events Fraction Percentage
  <chr>              <int>  <int>    <dbl>      <dbl>
1 NEGATIVE              18      3    0.167       16.7
2 POSITIVE              22     17    0.773       77.3
surv_object <-Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event)
KM_curve <- survfit(surv_object ~ ctDNA.Surveillance, data = circ_data,conf.int=0.95,conf.type="log-log") 
ggsurvplot(KM_curve, data = circ_data, pval = FALSE, conf.int = FALSE, risk.table = TRUE, break.time.by=6, palette=c("blue","red"), title="RFS - ctDNA at the Surveillance Window", ylab= "Recurrence-Free Survival", xlab="Time from Surgery (months)", legend.labs=c("ctDNA Negative", "ctDNA Positive"), legend.title="")

summary(KM_curve, times= c(0, 12, 24))
Call: survfit(formula = surv_object ~ ctDNA.Surveillance, data = circ_data, 
    conf.int = 0.95, conf.type = "log-log")

                ctDNA.Surveillance=NEGATIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     18       0    1.000  0.0000        1.000        1.000
   12     15       1    0.938  0.0605        0.632        0.991
   24      5       2    0.757  0.1277        0.401        0.919

                ctDNA.Surveillance=POSITIVE 
 time n.risk n.event survival std.err lower 95% CI upper 95% CI
    0     22       0    1.000   0.000        1.000        1.000
   12     12       9    0.591   0.105        0.361        0.762
   24      4       6    0.287   0.101        0.114        0.488
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"))
cox_fit <- coxph(surv_object ~ ctDNA.Surveillance, data=circ_data) 
ggforest(cox_fit,data = circ_data) 

summary(cox_fit)
Call:
coxph(formula = surv_object ~ ctDNA.Surveillance, data = circ_data)

  n= 40, number of events= 20 

                             coef exp(coef) se(coef)     z Pr(>|z|)   
ctDNA.SurveillancePOSITIVE 1.8586    6.4151   0.6287 2.956  0.00311 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                           exp(coef) exp(-coef) lower .95 upper .95
ctDNA.SurveillancePOSITIVE     6.415     0.1559     1.871     21.99

Concordance= 0.699  (se = 0.047 )
Likelihood ratio test= 12.46  on 1 df,   p=4e-04
Wald test            = 8.74  on 1 df,   p=0.003
Score (logrank) test = 11.47  on 1 df,   p=7e-04
cox_fit_summary <- summary(cox_fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 6.42 (1.87-21.99); p = 0.003"
circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels = c("NEGATIVE", "POSITIVE"), labels = c("Negative", "Positive"))
circ_data$RFS.Event <- factor(circ_data$RFS.Event, levels = c("FALSE", "TRUE"), labels = c("No Recurrence", "Recurrence"))
contingency_table <- table(circ_data$ctDNA.Surveillance, circ_data$RFS.Event)
chi_square_test <- chisq.test(contingency_table)
print(chi_square_test)

    Pearson's Chi-squared test with Yates' continuity correction

data:  contingency_table
X-squared = 12.222, df = 1, p-value = 0.0004722
fisher_exact_test <- fisher.test(contingency_table)
print(fisher_exact_test)

    Fisher's Exact Test for Count Data

data:  contingency_table
p-value = 0.0003284
alternative hypothesis: true odds ratio is not equal to 1
95 percent confidence interval:
   2.867735 118.989345
sample estimates:
odds ratio 
  15.45796 
print(contingency_table)
          
           No Recurrence Recurrence
  Negative            15          3
  Positive             5         17
table_df <- as.data.frame(contingency_table)
table_df$Total <- ave(table_df$Freq, table_df$Var1, FUN = sum)
table_df$Percentage <- table_df$Freq / table_df$Total
table_df$MiddlePercentage <- table_df$Percentage / 2
ggplot(table_df, aes(x = Var1, y = Percentage, fill = Var2)) +
  geom_bar(stat = "identity") +
  geom_text(aes(y = MiddlePercentage, label = Freq), position = "stack", color = "black", vjust = 1.5, size = 7) +
  theme_minimal() +
  labs(title = "ctDNA status at Surveillance", 
       x = "ctDNA", 
       y = "Patients (%)", 
       fill = "Recurrence",
       caption = paste("Fisher's exact test p-value: ", format.pval(fisher_exact_test$p.value))) +
  scale_y_continuous(labels = scales::percent_format()) +
  scale_fill_manual(values = c("No Recurrence" = "blue", "Recurrence" = "red")) + # define custom colors
  theme(axis.text.x = element_text(angle = 0, hjust = 1.5, size = 14), # increase x-axis text size
        axis.text.y = element_text(size = 14, color = "black"), # increase y-axis text size
        axis.title.x = element_text(size = 14, color = "black"), # increase x-axis label size
        axis.title.y = element_text(size = 14, color = "black"), # increase y-axis label size
        legend.text = element_text(size = 12, color = "black"))  # increase Recurrence label size

#Time-dependent analysis for RFS in longitudinal time points

rm(list=ls())
setwd("~/Downloads")
dt <- read_xlsx("CLIA UTUC Clinical Data_Time dependent.xlsx") |>
  clean_names() |>
  mutate(across(
    .cols = c(window_start_date, dfs_date, surveillance_1_date:surveillance_12_date),
    .fns = ~ as_date(as.Date(.x, format = "%Y-%m-%d"))
  )) |>
  filter(final_cohort == TRUE)

dt_biomarker <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date,
         surveillance_1_status:surveillance_12_date) |>
  filter(ct_dna_surveillance_available) |>
  pivot_longer(cols = surveillance_1_status:surveillance_12_date,
               names_to = c("visit_number", ".value"),
               names_pattern = "surveillance_(.)_(.*)") |>
  mutate(biomarker_time = day(days(date - window_start_date))) |>
  select(pts_id, biomarker_time, biomarker_status = status) |>
  filter(!is.na(biomarker_time))

glimpse(dt_biomarker)
Rows: 137
Columns: 3
$ pts_id           <chr> "UTUC-002", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-010", "UTUC-011", "UTUC-011", "UTUC-…
$ biomarker_time   <dbl> 133, 144, 181, 223, 267, 307, 349, 392, 433, 475, 74, 164, 204, 257, 385, 107, 145, 190, 267, 411, 78, 133, 267, 274, 341, 433, 42, 75, 21, 64, 111, 1…
$ biomarker_status <chr> "POSITIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "NEGATIVE", "POSITIVE", "POSITIVE", "POSITIVE", "NEGATIVE", "POSITIVE", "POSIT…
dt_survival <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date:dfs_date, dfs_event) |>  # Added dfs_event here
  filter(ct_dna_surveillance_available) |>
  mutate(dfs_time = (dfs_date - window_start_date),
         dfs_time = day(days(dfs_time)),
         dfs_event = as.numeric(dfs_event)) |>
  select(pts_id, dfs_time, dfs_event)

glimpse(dt_survival)
Rows: 38
Columns: 3
$ pts_id    <chr> "UTUC-002", "UTUC-010", "UTUC-011", "UTUC-012", "UTUC-015", "UTUC-026", "UTUC-027", "UTUC-028", "UTUC-030", "UTUC-031", "UTUC-032", "UTUC-034", "UTUC-035", "…
$ dfs_time  <dbl> 143, 707, 651, 636, 671, 90, 260, 13, 424, 74, 614, 697, 120, 314, 594, 210, 627, 23, 72, 1080, 773, 482, 201, 347, 893, 426, 324, 662, 182, 557, 77, 219, 61…
$ dfs_event <dbl> 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0
aux <- dt_survival %>% 
  filter(dfs_time <= 0)

tab <- left_join(aux, dt) |>
  select(pts_id, window_start_date, dfs_time, dfs_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = dfs_date:surveillance_12_date, 
                .fns = ~ as_date(.x))) |>
  select(pts_id, window_start_date, dfs_date, dfs_time)

datatable(tab, filter = "top")

dt_survival <- dt_survival |>
  filter(dfs_time > 0)

aux <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = surveillance_1_date:surveillance_12_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_12_date, 
                .fns = ~ .x < 0)) |>
  rowwise() |>
  mutate(sum_neg = 
           sum(c_across(surveillance_1_date:surveillance_12_date),
               na.rm = TRUE))  |>
  select(pts_id, sum_neg)

tab <- left_join(aux, dt) |>
  filter(sum_neg > 0) |>
  select(pts_id, sum_neg, window_start_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = window_start_date:surveillance_12_date, 
                .fns = ~ as_date(.x))) 

datatable(tab, filter = "top")

aux <- dt |>
  select(pts_id, ct_dna_surveillance_available,
         window_start_date, dfs_date,
         surveillance_1_date:surveillance_12_date) |>
  mutate(across(.cols = dfs_date:surveillance_12_date, 
                .fns = ~ .x - window_start_date)) |>
  mutate(across(.cols = surveillance_2_date:surveillance_12_date,
                .fns = ~ dfs_date < .x)) |>
  rowwise() |>
  mutate(n_biomarker_after_event = sum(c_across(surveillance_2_date:
                                                  surveillance_12_date), 
                                       na.rm = TRUE)) |>
  mutate(across(.cols = surveillance_1_date:surveillance_12_date,
                .fns = ~ !is.na(.x))) |>
  mutate(total_biomarker = sum(c_across(surveillance_2_date:
                                          surveillance_12_date), 
                               na.rm = TRUE)) |>
  select(pts_id, n_biomarker_after_event, total_biomarker)

temp <- aux |> 
  select(-pts_id) |> 
  group_by(n_biomarker_after_event, total_biomarker) |>  # Direct grouping
  summarise(freq = n(), .groups = "drop")  # Drop groups after summarization


tab <- left_join(aux, dt) |>
  select(pts_id, n_biomarker_after_event, total_biomarker, 
         dfs_date,
         surveillance_2_date:surveillance_12_date) |>
  mutate(across(.cols = dfs_date:surveillance_12_date, 
                .fns = ~ as_date(.x))) |>
  filter(n_biomarker_after_event > 0)
datatable(tab, filter = "top")

aux <- tmerge(data1 = dt_survival, 
              data2 = dt_survival,
              id = pts_id, 
              dfs_event = event(dfs_time, dfs_event))
dt_final <- tmerge(data1 = aux, 
                   data2 = dt_biomarker,
                   id = pts_id, 
                   biomarker_status = 
                     tdc(biomarker_time, biomarker_status))

datatable(dt_final, filter = "top")

# Syntax if there is not time-dependent covariate
# fit <- coxph(Surv(dfs_time, dfs_event) ~ biomarker_status,
#              data = dt_final)
# summary(fit)

fit <- coxph(Surv(tstart, tstop, dfs_event) ~ biomarker_status,
             data = dt_final)
summary(fit)
Call:
coxph(formula = Surv(tstart, tstop, dfs_event) ~ biomarker_status, 
    data = dt_final)

  n= 127, number of events= 17 
   (36 observations deleted due to missingness)

                            coef exp(coef) se(coef)     z Pr(>|z|)    
biomarker_statusPOSITIVE  2.8209   16.7919   0.6524 4.324 1.53e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

                         exp(coef) exp(-coef) lower .95 upper .95
biomarker_statusPOSITIVE     16.79    0.05955     4.675     60.32

Concordance= 0.824  (se = 0.055 )
Likelihood ratio test= 26.23  on 1 df,   p=3e-07
Wald test            = 18.7  on 1 df,   p=2e-05
Score (logrank) test = 31  on 1 df,   p=3e-08
cox_fit_summary <- summary(fit)

#Extract values for HR, 95% CI, and p-value
HR <- cox_fit_summary$coefficients[2]
lower_CI <- cox_fit_summary$conf.int[3]
upper_CI <- cox_fit_summary$conf.int[4]
p_value <- cox_fit_summary$coefficients[5]
label_text <- paste0("HR = ", round(HR, 2), " (", round(lower_CI, 2), "-", round(upper_CI, 2), "); p = ", round(p_value, 3))
print(label_text)
[1] "HR = 16.79 (4.67-60.32); p = 0"

#Median numbers of time points in the longitudinal setting

# Load the dataset
rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]
circ_datadf <- as.data.frame(circ_data)

median_Nsurvtps <- median(circ_datadf$Nsurvtps, na.rm = TRUE)
min_Nsurvtps <- min(circ_datadf$Nsurvtps, na.rm = TRUE)
max_Nsurvtps <- max(circ_datadf$Nsurvtps, na.rm = TRUE)

cat(sprintf("Median # of surveillance time points: %d (%d-%d)\n", 
            median_Nsurvtps, min_Nsurvtps, max_Nsurvtps))
Median # of surveillance time points: 3 (1-12)

#Median of median interval of ctDNA timepoints and radiological imaging assessment

rm(list=ls())
setwd("~/Downloads")
df <- read.csv("RWE UTUC_OP 102025.csv", stringsAsFactors = FALSE)

names(df) <- trimws(names(df))

# ---- Helper: compute median interval per patient ----
median_interval_per_patient <- function(data, filter_col, filter_val) {
  data %>%
    filter(!!sym(filter_col) == filter_val) %>%
    arrange(PatientName, date.diff) %>%
    group_by(PatientName) %>%
    mutate(interval = date.diff - lag(date.diff)) %>%
    summarise(median_interval = median(interval, na.rm = TRUE), .groups = "drop") %>%
    mutate(event_group = filter_val)
}

# ---- Compute per-patient medians ----
ctDNA_patient_medians   <- median_interval_per_patient(df, "Event", "ctDNA")
imaging_patient_medians <- median_interval_per_patient(df, "Event_type", "Imaging")

# ---- Function to summarize patient-level medians to cohort-level ----
cohort_summary <- function(patient_data, event_label) {
  # Filter out patients with NA median intervals
  valid_data <- patient_data %>% filter(!is.na(median_interval))
  
  data.frame(
    Event_Type = event_label,
    Cohort_Median_Frequency = median(valid_data$median_interval, na.rm = TRUE),
    Min_Patient_Median = min(valid_data$median_interval, na.rm = TRUE),
    Max_Patient_Median = max(valid_data$median_interval, na.rm = TRUE),
    Range_Patient_Median = max(valid_data$median_interval, na.rm = TRUE) - 
      min(valid_data$median_interval, na.rm = TRUE)
  )
}

# ---- Combine all results ----
results <- bind_rows(
  cohort_summary(ctDNA_patient_medians, "ctDNA"),
  cohort_summary(imaging_patient_medians, "Imaging")
)

# ---- Print cohort-level summary ----
cat("===== Median Frequency (Days) per Cohort =====\n")
===== Median Frequency (Days) per Cohort =====
print(results, row.names = FALSE)
 Event_Type Cohort_Median_Frequency Min_Patient_Median Max_Patient_Median Range_Patient_Median
      ctDNA                   75.75                 14                156                  142
    Imaging                  154.25                  0                932                  932
# ---- OPTIONAL: Save patient-level medians ----
patient_level_medians <- bind_rows(ctDNA_patient_medians, imaging_patient_medians)
#write.csv(patient_level_medians, "patient_median_intervals.csv", row.names = FALSE)

#Plot for individual lead-time calculations for each pt

rm(list=ls())
csv_path <- "~/Downloads/CLIA UTUC_Lead Time pts.csv"
df <- read.csv(csv_path, check.names = FALSE)
if ("Final.cohort" %in% names(df)) {
  df <- df[df$Final.cohort == TRUE, ]
} else {
  warning("Column 'Final.cohort' not found in the dataset.")
}
id_candidates <- c("Patient","ID","Pt","Subject","Sample")
id_col <- id_candidates[id_candidates %in% names(df)][1]
if (is.na(id_col)) {
  df <- df %>% mutate(Patient = row_number())
  id_col <- "Patient"
}

num_cols <- intersect(c("MR","CR","LT"), names(df))
df[num_cols] <- lapply(df[num_cols], function(x) suppressWarnings(as.numeric(x)))

# If LT missing, compute in days
if (!("LT" %in% names(df))) {
  df <- df %>% mutate(LT = CR - MR)
}

# ---- convert to months ----
# Approximate: 30.44 days per month (average)
days_to_months <- function(x) x / 30.437

df <- df %>%
  mutate(MR_mo = days_to_months(MR),
         CR_mo = days_to_months(CR),
         LT_mo = days_to_months(LT))

# ---- ordering for y-axis ----
df <- df %>%
  mutate(Earliest = pmin(MR_mo, CR_mo, na.rm = TRUE)) %>%
  arrange(Earliest) %>%
  mutate(Patient_f = factor(.data[[id_col]], levels = .data[[id_col]]))

# ---- long format for points ----
points_long <- df %>%
  select(Patient_f, MR_mo, CR_mo) %>%
  pivot_longer(c(MR_mo, CR_mo), names_to = "Type", values_to = "Months") %>%
  mutate(Type = dplyr::recode(Type,
                              "MR_mo" = "Molecular recurrence",
                              "CR_mo" = "Clinical recurrence"))

# ---- segments between MR and CR ----
segments_df <- df %>%
  transmute(Patient_f,
            x0 = MR_mo, x1 = CR_mo,
            lt_flag = if_else(LT > 120, "gt120", "le120"))

# ---- annotation ----
med_lt  <- median(df$LT_mo, na.rm = TRUE)
min_lt  <- min(df$LT_mo, na.rm = TRUE)
max_lt  <- max(df$LT_mo, na.rm = TRUE)

annot_label <- sprintf("Median lead-time:\n%.1f months (%.1f to %.1f)",
                       med_lt, min_lt, max_lt)

# ---- plot ----
pal <- c("Molecular recurrence" = "#10B4C1",
         "Clinical recurrence"  = "#C96A72")

x_max <- max(c(df$MR_mo, df$CR_mo), na.rm = TRUE)
y_mid <- levels(df$Patient_f)[ceiling(nlevels(df$Patient_f) * 0.55)]

p <- ggplot() +
  geom_segment(data = segments_df,
               aes(x = x0, xend = x1, y = Patient_f, yend = Patient_f,
                   linetype = lt_flag),
               linewidth = 0.6, color = "black") +
  scale_linetype_manual(values = c(le120 = "solid", gt120 = "dashed"),
                        guide = "none") +
  geom_point(data = points_long,
             aes(x = Months, y = Patient_f, color = Type),
             size = 2.8) +
  scale_color_manual(values = pal, name = NULL) +
  labs(x = "Months from Surgery or Definitive Treatment", y = NULL) +
  annotate("text",
           x = x_max * 0.73,
           y = y_mid,
           label = annot_label,
           hjust = 0, vjust = 0.5, size = 3.8) +
  scale_x_continuous(breaks = seq(0, ceiling(x_max/3)*3, by = 3)) +
  coord_cartesian(xlim = c(0, x_max * 1.05)) +
  theme_bw(base_size = 12) +
  theme(
    panel.grid.major = element_line(color = "grey85", linewidth = 0.3),
    panel.grid.minor = element_line(color = "grey92", linewidth = 0.2),
    axis.text.y = element_text(size = 9)
  )

print(p)


wc_df <- df %>% dplyr::select(MR, CR) %>% tidyr::drop_na()
paired_diff <- wc_df$CR - wc_df$MR

wilx <- wilcox.test(
  x = wc_df$CR, y = wc_df$MR,
  paired = TRUE,
  alternative = "two.sided",
  conf.int = TRUE,      # gives CI for the Hodges–Lehmann estimate of the median difference
  exact = FALSE         # safer for ties/large N
)

W_stat   <- unname(wilx$statistic)          # Wilcoxon V
p_value  <- wilx$p.value
HL_est   <- unname(wilx$estimate)           # median of (CR - MR)
HL_ci    <- wilx$conf.int                   # CI for median difference

# Simple p-value formatter for annotation/publication
fmt_p <- function(p) {
  if (is.na(p)) return("NA")
  if (p < 0.001) "< 0.001" else sprintf("= %.3f", round(p, 3))
}
p_text <- paste0("P ", fmt_p(p_value))  # e.g., "P = 0.003" or "P < 0.001"

# Optional: print a compact summary
cat("\nPaired Wilcoxon signed-rank test (CR vs MR)\n",
    "-------------------------------------------\n",
    sprintf("N pairs: %d", nrow(wc_df)), "\n",
    sprintf("W (V): %g", W_stat), "\n",
    sprintf("P-value: %s", ifelse(p_value < 0.001, "< 0.001", sprintf("%.6f", p_value))), "\n",
    sprintf("Hodges–Lehmann median difference (CR - MR): %.1f days", HL_est), "\n",
    sprintf("95%% CI: [%.1f, %.1f] days", HL_ci[1], HL_ci[2]), "\n", sep = "")

Paired Wilcoxon signed-rank test (CR vs MR)
-------------------------------------------
N pairs: 21
W (V): 231
P-value: < 0.001
Hodges–Lehmann median difference (CR - MR): 155.5 days
95% CI: [86.0, 241.5] days

#Multivariate cox regression at Surveillance Window for RFS

rm(list=ls())
setwd("~/Downloads") 
circ_data <- read.csv("RWE UTUC_Clinical Data 102025.csv")
circ_data <- circ_data[circ_data$Final.cohort=="TRUE",]
circ_data <- circ_data[circ_data$ctDNA.Surveillance!="",]

circ_data$ctDNA.Surveillance <- factor(circ_data$ctDNA.Surveillance, levels=c("NEGATIVE","POSITIVE"), labels = c("Negative", "Positive"))
circ_data$cStage <- factor(circ_data$cStage, levels = c("0/I", "II/III"))
circ_data$NAC <- factor(circ_data$NAC, levels = c("FALSE", "TRUE"))
circ_data$ACT <- factor(circ_data$ACT, levels = c("FALSE", "TRUE"))
surv_object <- Surv(time = circ_data$RFS.months, event = circ_data$RFS.Event) 
cox_fit <- coxph(surv_object ~ ctDNA.Surveillance + Age + cStage + NAC + ACT, data=circ_data) 
ggforest(cox_fit, data = circ_data, main = "Multivariate Regression Model for RFS - All Stages", refLabel = "Reference Group")

test.ph <- cox.zph(cox_fit)

#ctDNA and MTM/mL Dynamics for pts at surveillance window

#Dynamics and MTM/mL plots for patients with ctDNA negative at surveillance
rm(list=ls())
setwd("~/Downloads")
df <- read.csv("CLIA UTUC ctDNA MTM.csv", stringsAsFactors = FALSE)
df <- df[df$Final.cohort=="TRUE",]
df <- df[df$ctDNA.Surveillance=="NEGATIVE",]

df$RFS.Event <- ifelse(df$RFS.Event %in% c("No", "no", "FALSE", "False", "0"), FALSE,
                       ifelse(df$RFS.Event %in% c("Yes", "yes", "TRUE", "True", "1"), TRUE, NA))
df$RFS.Event <- factor(df$RFS.Event, levels = c(FALSE, TRUE))
df <- df %>%
  group_by(PatientName) %>%
  filter(n() >= 2) %>% #keep only pts with at least 2 post-surgery time points
  ungroup()

num_unique <- length(unique(df$PatientName))
cat("Number of unique patients:", num_unique, "\n")
Number of unique patients: 17 
df_patient_pfs <- df %>%
  group_by(PatientName) %>%
  dplyr::summarize(
    PFS_True = any(RFS.Event == TRUE, na.rm = TRUE),
    PFS_False = all(RFS.Event == FALSE, na.rm = TRUE)
  )

num_true <- sum(df_patient_pfs$PFS_True)
num_false <- sum(df_patient_pfs$PFS_False)

cat("Number of unique patients with Event:", num_true, "\n")
Number of unique patients with Event: 3 
cat("Number of unique patients with No Event:", num_false, "\n")
Number of unique patients with No Event: 14 
p <- ggplot(df, aes(x = date.diff.months, 
                    y = MTM.mL, 
                    group = PatientName, 
                    color = RFS.Event)) +
  geom_line() +      # Connect timepoints for each patient
  geom_point() +     # Add points for each timepoint
  # Use a log10 scale for the y-axis with specified breaks
  scale_y_log10(breaks = c(0.01, 0.1, 1, 10, 100, 1000),
                labels = c("0.01","0.1", "1", "10", "100", "1000")) +
  scale_x_continuous(breaks = seq(0, max(df$date.diff.months, na.rm = TRUE), by = 6)) +
  scale_color_manual(values = c("FALSE" = "blue", "TRUE" = "red")) +
  labs(
    x = "Time Since Surgery or start of definitive treatment (months)",
    y = "Mean Tumor Molecules per mL (MTM/mL)",
    color = "RFS Event"
  ) +
  theme_minimal()
print(p)


#Dynamics and MTM/mL plots for patients with ctDNA positive at surveillance
rm(list=ls())
setwd("~/Downloads")
df <- read.csv("CLIA UTUC ctDNA MTM.csv", stringsAsFactors = FALSE)
df <- df[df$Final.cohort=="TRUE",]
df <- df[df$ctDNA.Surveillance=="POSITIVE",]

df$RFS.Event <- ifelse(df$RFS.Event %in% c("No", "no", "FALSE", "False", "0"), FALSE,
                       ifelse(df$RFS.Event %in% c("Yes", "yes", "TRUE", "True", "1"), TRUE, NA))
df$RFS.Event <- factor(df$RFS.Event, levels = c(FALSE, TRUE))
df <- df %>%
  group_by(PatientName) %>%
  filter(n() >= 2) %>% #keep only pts with at least 2 post-surgery time points
  ungroup()

num_unique <- length(unique(df$PatientName))
cat("Number of unique patients:", num_unique, "\n")
Number of unique patients: 21 
df_patient_pfs <- df %>%
  group_by(PatientName) %>%
  dplyr::summarize(
    PFS_True = any(RFS.Event == TRUE, na.rm = TRUE),
    PFS_False = all(RFS.Event == FALSE, na.rm = TRUE)
  )

num_true <- sum(df_patient_pfs$PFS_True)
num_false <- sum(df_patient_pfs$PFS_False)

cat("Number of unique patients with Event:", num_true, "\n")
Number of unique patients with Event: 16 
cat("Number of unique patients with No Event:", num_false, "\n")
Number of unique patients with No Event: 5 
p <- ggplot(df, aes(x = date.diff.months, 
                    y = MTM.mL, 
                    group = PatientName, 
                    color = RFS.Event)) +
  geom_line() +      # Connect timepoints for each patient
  geom_point() +     # Add points for each timepoint
  # Use a log10 scale for the y-axis with specified breaks
  scale_y_log10(breaks = c(0.01, 0.1, 1, 10, 100, 1000),
                labels = c("0.01","0.1", "1", "10", "100", "1000")) +
  scale_x_continuous(breaks = seq(0, max(df$date.diff.months, na.rm = TRUE), by = 6)) +
  scale_color_manual(values = c("FALSE" = "blue", "TRUE" = "red")) +
  labs(
    x = "Time Since Surgery or start of definitive treatment (months)",
    y = "Mean Tumor Molecules per mL (MTM/mL)",
    color = "RFS Event"
  ) +
  theme_minimal()
print(p)

#ctDNA velocity and lead time liner regression

rm(list=ls())
csv_path <- "~/Downloads/CLIA UTUC_ctDNA velocity.csv"  # <- adjust if needed
df_raw <- read.csv(csv_path, check.names = FALSE)
df <- df_raw %>%
  rename(MTM_mL = `MTM.mL`) %>%
  mutate(
    daysCR.months = as.numeric(daysCR.months),
    MTM_mL = as.numeric(MTM_mL)
  ) %>%
  filter(Final.cohort == TRUE, !is.na(PatientName), !is.na(daysCR.months), !is.na(MTM_mL))

preCR <- df %>%
  filter(daysCR.months <= 0, is.finite(MTM_mL), MTM_mL > 0)

eligible <- preCR %>%
  group_by(PatientName) %>%
  filter(n() >= 2, n_distinct(daysCR.months) >= 2) %>%
  ungroup()
if (nrow(eligible) == 0) {
  warning("No patients have ≥2 valid pre-recurrence points with distinct times; regression lines will be omitted.")
}

fits <- eligible %>%
  group_by(PatientName) %>%
  summarise(x_min = min(daysCR.months, na.rm = TRUE), .groups = "drop") %>%
  mutate(grid = map(x_min, ~seq(.x, 0, length.out = 50))) %>%
  select(PatientName, grid)

predict_patient <- function(dat, newx) {
  if (length(newx) == 0) {
    return(tibble(daysCR.months = numeric(0), MTM_mL = numeric(0)))
  }
  dat2 <- dat %>%
    filter(is.finite(MTM_mL), MTM_mL > 0) %>%
    mutate(log_ctdna = log10(MTM_mL))
  if (nrow(dat2) < 2 || n_distinct(dat2$daysCR.months) < 2 || any(!is.finite(dat2$log_ctdna))) {
    return(tibble(daysCR.months = numeric(0), MTM_mL = numeric(0)))
  }
  m <- lm(log_ctdna ~ daysCR.months, data = dat2)
  tibble(
    daysCR.months = newx,
    MTM_mL = 10 ^ predict(m, newdata = tibble(daysCR.months = newx))
  )
}

pred_lines <- eligible %>%
  group_by(PatientName) %>%
  tidyr::nest() %>%                       # list-column "data" per patient
  left_join(fits, by = "PatientName") %>% # list-column "grid" per patient
  mutate(pred = map2(data, grid, ~predict_patient(.x, .y))) %>%
  select(PatientName, pred) %>%
  tidyr::unnest(pred)

pooled_line <- {
  if (nrow(preCR) >= 2 && n_distinct(preCR$daysCR.months) >= 2) {
    pooled_x <- seq(min(preCR$daysCR.months, na.rm = TRUE), 0, length.out = 100)
    pooled_fit <- lm(log10(MTM_mL) ~ daysCR.months, data = preCR)
    tibble(
      daysCR.months = pooled_x,
      MTM_mL = 10 ^ predict(pooled_fit, newdata = tibble(daysCR.months = pooled_x))
    )
  } else {
    tibble(daysCR.months = numeric(0), MTM_mL = numeric(0))
  }
}

x_min <- floor(min(df$daysCR.months, na.rm = TRUE) / 3) * 3
x_max <- ceiling(max(df$daysCR.months, na.rm = TRUE) / 3) * 3
y_min_pos <- max(min(df$MTM_mL[df$MTM_mL > 0], na.rm = TRUE) / 2, 0.01)
y_max_pos <- 10 ^ ceiling(log10(max(df$MTM_mL, na.rm = TRUE)))
log_breaks <- 10 ^ seq(floor(log10(y_min_pos)), ceiling(log10(y_max_pos)))

p <- ggplot() +
  # per-patient fitted lines (if any)
  geom_line(data = pred_lines,
            aes(x = daysCR.months, y = MTM_mL, color = PatientName),
            linewidth = 1) +
  # pooled dashed trend (if any)
  geom_line(data = pooled_line,
            aes(x = daysCR.months, y = MTM_mL),
            linewidth = 0.8, linetype = "dashed", color = "grey35") +
  # raw points (all timepoints, before and after)
  geom_point(data = df,
             aes(x = daysCR.months, y = MTM_mL, color = PatientName),
             size = 1.8, alpha = 0.85) +
  # vertical line at imaging-positive date
  geom_vline(xintercept = 0, linetype = "dashed", linewidth = 0.8, color = "black") +
  scale_y_log10(breaks = log_breaks,
                labels = label_number(accuracy = 1)) +
  scale_x_continuous(breaks = seq(x_min, x_max, by = 3)) +  # every 3 months
  labs(
    x = "Months before/after clinical recurrence (0 = imaging positive)",
    y = "ctDNA level (MTM/mL)",
    color = "Patient"
  ) +
  theme_bw(base_size = 12) +
  theme(
    panel.grid.major = element_line(color = "grey85", linewidth = 0.3),
    panel.grid.minor = element_line(color = "grey92", linewidth = 0.2),
    legend.position = "none"  # change to "right" if you want the legend
  )

print(p)

#Time from first ctDNA positive timepoint to last imaging in surveillance_False Positive patients

rm(list=ls())
csv_path <- "~/Downloads/CLIA UTUC_Time from ctDNA to imaging FP pts.csv"
df <- read.csv(csv_path, check.names = FALSE)
if ("Final.cohort" %in% names(df)) {
  df <- df[df$Final.cohort == TRUE, ]
} else {
  warning("Column 'Final.cohort' not found in the dataset.")
}
id_candidates <- c("Patient","ID","Pt","Subject","Sample")
id_col <- id_candidates[id_candidates %in% names(df)][1]
if (is.na(id_col)) {
  df <- df %>% mutate(Patient = row_number())
  id_col <- "Patient"
}

num_cols <- intersect(c("MR","CR","LT"), names(df))
df[num_cols] <- lapply(df[num_cols], function(x) suppressWarnings(as.numeric(x)))

# If LT missing, compute in days
if (!("LT" %in% names(df))) {
  df <- df %>% mutate(LT = CR - MR)
}

# ---- convert to months ----
# Approximate: 30.44 days per month (average)
days_to_months <- function(x) x / 30.437

df <- df %>%
  mutate(MR_mo = days_to_months(MR),
         CR_mo = days_to_months(CR),
         LT_mo = days_to_months(LT))

# ---- ordering for y-axis ----
df <- df %>%
  mutate(Earliest = pmin(MR_mo, CR_mo, na.rm = TRUE)) %>%
  arrange(Earliest) %>%
  mutate(Patient_f = factor(.data[[id_col]], levels = .data[[id_col]]))

# ---- long format for points ----
points_long <- df %>%
  select(Patient_f, MR_mo, CR_mo) %>%
  pivot_longer(c(MR_mo, CR_mo), names_to = "Type", values_to = "Months") %>%
  mutate(Type = dplyr::recode(Type,
                              "MR_mo" = "First ctDNA positive",
                              "CR_mo" = "Last Radiological assessment"))

# ---- segments between MR and CR ----
segments_df <- df %>%
  transmute(Patient_f,
            x0 = MR_mo, x1 = CR_mo,
            lt_flag = if_else(LT > 120, "gt120", "le120"))

# ---- annotation ----
med_lt  <- median(df$LT_mo, na.rm = TRUE)
min_lt  <- min(df$LT_mo, na.rm = TRUE)
max_lt  <- max(df$LT_mo, na.rm = TRUE)

annot_label <- sprintf("Median time from ctDNA positive to last imaging:\n%.1f months (%.1f to %.1f)",
                       med_lt, min_lt, max_lt)

# ---- plot ----
pal <- c("First ctDNA positive" = "black",
         "Last Radiological assessment"  = "red")

x_max <- max(c(df$MR_mo, df$CR_mo), na.rm = TRUE)
y_mid <- levels(df$Patient_f)[ceiling(nlevels(df$Patient_f) * 0.55)]

p <- ggplot() +
  geom_segment(data = segments_df,
               aes(x = x0, xend = x1, y = Patient_f, yend = Patient_f,
                   linetype = lt_flag),
               linewidth = 0.6, color = "black") +
  scale_linetype_manual(values = c(le120 = "solid", gt120 = "dashed"),
                        guide = "none") +
  geom_point(data = points_long,
             aes(x = Months, y = Patient_f, color = Type),
             size = 2.8) +
  scale_color_manual(values = pal, name = NULL) +
  labs(x = "Months from ctDNA positivity", y = NULL) +
  annotate("text",
           x = x_max * 0.73,
           y = y_mid,
           label = annot_label,
           hjust = 0, vjust = 0.5, size = 3.8) +
  scale_x_continuous(breaks = seq(0, ceiling(x_max/3)*3, by = 3)) +
  coord_cartesian(xlim = c(0, x_max * 1.05)) +
  theme_bw(base_size = 12) +
  theme(
    panel.grid.major = element_line(color = "grey85", linewidth = 0.3),
    panel.grid.minor = element_line(color = "grey92", linewidth = 0.2),
    axis.text.y = element_text(size = 9)
  )

print(p)


wc_df <- df %>% dplyr::select(MR, CR) %>% tidyr::drop_na()
paired_diff <- wc_df$CR - wc_df$MR

wilx <- wilcox.test(
  x = wc_df$CR, y = wc_df$MR,
  paired = TRUE,
  alternative = "two.sided",
  conf.int = TRUE,      # gives CI for the Hodges–Lehmann estimate of the median difference
  exact = FALSE         # safer for ties/large N
)

W_stat   <- unname(wilx$statistic)          # Wilcoxon V
p_value  <- wilx$p.value
HL_est   <- unname(wilx$estimate)           # median of (CR - MR)
HL_ci    <- wilx$conf.int                   # CI for median difference

# Simple p-value formatter for annotation/publication
fmt_p <- function(p) {
  if (is.na(p)) return("NA")
  if (p < 0.001) "< 0.001" else sprintf("= %.3f", round(p, 3))
}
p_text <- paste0("P ", fmt_p(p_value))  # e.g., "P = 0.003" or "P < 0.001"

# Optional: print a compact summary
cat("\nPaired Wilcoxon signed-rank test (CR vs MR)\n",
    "-------------------------------------------\n",
    sprintf("N pairs: %d", nrow(wc_df)), "\n",
    sprintf("W (V): %g", W_stat), "\n",
    sprintf("P-value: %s", ifelse(p_value < 0.001, "< 0.001", sprintf("%.6f", p_value))), "\n",
    sprintf("Hodges–Lehmann median difference (CR - MR): %.1f days", HL_est), "\n",
    sprintf("95%% CI: [%.1f, %.1f] days", HL_ci[1], HL_ci[2]), "\n", sep = "")

Paired Wilcoxon signed-rank test (CR vs MR)
-------------------------------------------
N pairs: 4
W (V): 10
P-value: 0.100348
Hodges–Lehmann median difference (CR - MR): 27.7 days
95% CI: [6.0, 291.0] days

#Individual Pt Plots_False Positive patients

#Filter for Patient UTUC-010
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Pt Specific Plot data.csv")
plot_data <- circ_data %>% 
  filter(PatientName == "UTUC-010") 

# 2. Process layers
# Handle ctDNA log-scale pseudo-zero
data_Signatera <- plot_data %>% 
  filter(Event_type %in% c("ctDNA_pos", "ctDNA_neg")) %>%
  mutate(MTM_Log = ifelse(MTM.mL == 0, 0.001, MTM.mL))

data_Imaging <- plot_data %>% 
  filter(Event_type == "Imaging")

# Treatment blocks
data_Treatment <- plot_data %>% 
  filter(!is.na(Tx_type) & Tx_type != "NA" & !is.na(Tx_start.months)) %>%
  distinct(Tx_type, Tx_start.months, Tx_end.months)

# 3. Create the Plot
ggplot() +
  # Treatment Rectangles
  geom_rect(data = data_Treatment, 
            aes(xmin = Tx_start.months, xmax = Tx_end.months, ymin = 0.004, ymax = 10), 
            fill = "lightblue", alpha = 0.15) +
  geom_text(data = data_Treatment, 
            aes(x = (Tx_start.months + Tx_end.months)/2, y = 5, label = Tx_type), 
            angle = 90, color = "black", size = 3, fontface = "bold") +
  
  # ctDNA Line
  geom_line(data = data_Signatera, 
            aes(x = date.diff.months, y = MTM_Log), color = "black", linewidth = 0.7) +
  
  # ctDNA Points (Mapped to Fill)
  geom_point(data = data_Signatera, 
             aes(x = date.diff.months, y = MTM_Log, fill = Event_type), 
             shape = 21, size = 3.5, color = "black") +
  
  # Imaging Points (Mapped to Shape and Color to create a second legend)
  geom_point(data = data_Imaging, 
             aes(x = date.diff.months, y = 0.005, color = "NED in Scan"), 
             shape = 25, size = 4, fill = "darkgreen") +
  
  # X-Axis: 3-month intervals
  scale_x_continuous(breaks = seq(0, max(plot_data$date.diff.months, na.rm=T) + 3, by = 3)) +
  
  # Y-Axis: Log10 scale
  scale_y_log10(breaks = c(0.001, 0.01, 0.1, 1, 10),
                labels = c("0", "0.01", "0.1", "1", "10"),
                limits = c(0.001, 15)) +
  
  # Legend for ctDNA
  scale_fill_manual(values = c("ctDNA_pos" = "black", "ctDNA_neg" = "white"), 
                    labels = c("Negative", "Positive"), 
                    name = "ctDNA Status") +
  
  # Legend for Imaging
  scale_color_manual(values = c("NED in Scan" = "darkgreen"), 
                     name = "Imaging Result") +
  
  # Styling the legend to show the triangle correctly
  guides(color = guide_legend(override.aes = list(shape = 25, fill = "darkgreen"))) +
  
  labs(x = "Months from Surgery", 
       y = "MTM/mL", 
       title = "Clinical Timeline: UTUC-010") +
  theme_minimal() +
  theme(panel.grid.minor = element_blank(),
        legend.position = "right")


#Filter for Patient UTUC-011
rm(list=ls())
setwd("~/Downloads")
circ_data <- read.csv("RWE UTUC_Pt Specific Plot data.csv")
plot_data <- circ_data %>% 
  filter(PatientName == "UTUC-011") 

# 2. Process layers
# Handle ctDNA log-scale pseudo-zero
data_Signatera <- plot_data %>% 
  filter(Event_type %in% c("ctDNA_pos", "ctDNA_neg")) %>%
  mutate(MTM_Log = ifelse(MTM.mL == 0, 0.001, MTM.mL))

data_Imaging <- plot_data %>% 
  filter(Event_type == "Imaging")

# Treatment blocks
data_Treatment <- plot_data %>% 
  filter(!is.na(Tx_type) & Tx_type != "NA" & !is.na(Tx_start.months)) %>%
  distinct(Tx_type, Tx_start.months, Tx_end.months)

# 3. Create the Plot
ggplot() +
  # Treatment Rectangles
  geom_rect(data = data_Treatment, 
            aes(xmin = Tx_start.months, xmax = Tx_end.months, ymin = 0.004, ymax = 10), 
            fill = "lightblue", alpha = 0.15) +
  geom_text(data = data_Treatment, 
            aes(x = (Tx_start.months + Tx_end.months)/2, y = 5, label = Tx_type), 
            angle = 90, color = "black", size = 3, fontface = "bold") +
  
  # ctDNA Line
  geom_line(data = data_Signatera, 
            aes(x = date.diff.months, y = MTM_Log), color = "black", linewidth = 0.7) +
  
  # ctDNA Points (Mapped to Fill)
  geom_point(data = data_Signatera, 
             aes(x = date.diff.months, y = MTM_Log, fill = Event_type), 
             shape = 21, size = 3.5, color = "black") +
  
  # Imaging Points (Mapped to Shape and Color to create a second legend)
  geom_point(data = data_Imaging, 
             aes(x = date.diff.months, y = 0.005, color = "NED in Scan"), 
             shape = 25, size = 4, fill = "darkgreen") +
  
  # X-Axis: 3-month intervals
  scale_x_continuous(breaks = seq(0, max(plot_data$date.diff.months, na.rm=T) + 3, by = 3)) +
  
  # Y-Axis: Log10 scale
  scale_y_log10(breaks = c(0.001, 0.01, 0.1, 1, 10),
                labels = c("0", "0.01", "0.1", "1", "10"),
                limits = c(0.001, 15)) +
  
  # Legend for ctDNA
  scale_fill_manual(values = c("ctDNA_pos" = "black", "ctDNA_neg" = "white"), 
                    labels = c("Negative", "Positive"), 
                    name = "ctDNA Status") +
  
  # Legend for Imaging
  scale_color_manual(values = c("NED in Scan" = "darkgreen"), 
                     name = "Imaging Result") +
  
  # Styling the legend to show the triangle correctly
  guides(color = guide_legend(override.aes = list(shape = 25, fill = "darkgreen"))) +
  
  labs(x = "Months from Surgery", 
       y = "MTM/mL", 
       title = "Clinical Timeline: UTUC-011") +
  theme_minimal() +
  theme(panel.grid.minor = element_blank(),
        legend.position = "right")

LS0tCnRpdGxlOiAiQXlhbmFtYmFra2FtIGV0IGFsX2N0RE5BIGluIFVUVUMgQ2xpbmljYWwgQW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KbGlicmFyeShzd2ltcGxvdCkKbGlicmFyeShncmlkKQpsaWJyYXJ5KGd0YWJsZSkKbGlicmFyeShyZWFkcikgCmxpYnJhcnkobW9zYWljKQpsaWJyYXJ5KGRwbHlyKSAKbGlicmFyeShzdXJ2aXZhbCkgCmxpYnJhcnkoc3Vydm1pbmVyKSAKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShjb3hwaGYpCmxpYnJhcnkoZ2d0aGVtZXMpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGd0c3VtbWFyeSkKbGlicmFyeShmbGV4dGFibGUpCmxpYnJhcnkocGFyYW1ldGVycykKbGlicmFyeShjYXIpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShqYW5pdG9yKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkocm1zKQoKI2N0RE5BIERldGVjdGlvbiBSYXRlcyBieSBXaW5kb3cgYW5kIFN0YWdlcwpgYGB7cn0KI2N0RE5BIGF0IE1SRCBieSBTdGFnZQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5NUkQhPSIiLF0KY2lyY19kYXRhJGN0RE5BLk1SRCA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLk1SRCwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY2lyY19kYXRhIDwtIHN1YnNldChjaXJjX2RhdGEsIGN0RE5BLk1SRCAlaW4lIGMoIk5FR0FUSVZFIiwgIlBPU0lUSVZFIikpCmNpcmNfZGF0YSRTdGFnZSA8LSBmYWN0b3IoY2lyY19kYXRhJFN0YWdlLCBsZXZlbHM9YygiMC9JIiwiSUkiLCJJSUkiKSkKcG9zaXRpdmVfY291bnRzX2J5X3N0YWdlIDwtIGFnZ3JlZ2F0ZShjaXJjX2RhdGEkY3RETkEuTVJEID09ICJQT1NJVElWRSIsIGJ5PWxpc3QoY2lyY19kYXRhJFN0YWdlKSwgRlVOPXN1bSkKdG90YWxfY291bnRzX2J5X3N0YWdlIDwtIGFnZ3JlZ2F0ZShjaXJjX2RhdGEkY3RETkEuTVJELCBieT1saXN0KGNpcmNfZGF0YSRTdGFnZSksIEZVTj1sZW5ndGgpCmNvbWJpbmVkX2RhdGEgPC0gZGF0YS5mcmFtZSgKICBTdGFnZSA9IHRvdGFsX2NvdW50c19ieV9zdGFnZSRHcm91cC4xLAogIFRvdGFsX0NvdW50ID0gdG90YWxfY291bnRzX2J5X3N0YWdlJHgsCiAgUG9zaXRpdmVfQ291bnQgPSBwb3NpdGl2ZV9jb3VudHNfYnlfc3RhZ2UkeCwKICBSYXRlID0gKHBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSR4IC8gdG90YWxfY291bnRzX2J5X3N0YWdlJHgpICogMTAwICAjIENvbnZlcnQgdG8gcGVyY2VudGFnZQopCmNvbWJpbmVkX2RhdGEkUmF0ZSA8LSBzcHJpbnRmKCIlLjJmJSUiLCBjb21iaW5lZF9kYXRhJFJhdGUpCm92ZXJhbGxfdG90YWxfY291bnQgPC0gbnJvdyhjaXJjX2RhdGEpCm92ZXJhbGxfcG9zaXRpdmVfY291bnQgPC0gbnJvdyhjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLk1SRCA9PSAiUE9TSVRJVkUiLF0pCm92ZXJhbGxfcG9zaXRpdml0eV9yYXRlIDwtIChvdmVyYWxsX3Bvc2l0aXZlX2NvdW50IC8gb3ZlcmFsbF90b3RhbF9jb3VudCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCm92ZXJhbGxfcm93IDwtIGRhdGEuZnJhbWUoCiAgU3RhZ2UgPSAiT3ZlcmFsbCIsCiAgVG90YWxfQ291bnQgPSBvdmVyYWxsX3RvdGFsX2NvdW50LAogIFBvc2l0aXZlX0NvdW50ID0gb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCwKICBSYXRlID0gc3ByaW50ZigiJS4yZiUlIiwgb3ZlcmFsbF9wb3NpdGl2aXR5X3JhdGUpCikKY29tYmluZWRfZGF0YSA8LSByYmluZChjb21iaW5lZF9kYXRhLCBvdmVyYWxsX3JvdykKcHJpbnQoY29tYmluZWRfZGF0YSkKCiNjdEROQSBhdCBNUkQgYnkgTkFDIHN0YXR1cwpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5NUkQhPSIiLF0KY2lyY19kYXRhJGN0RE5BLk1SRCA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLk1SRCwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY2lyY19kYXRhIDwtIHN1YnNldChjaXJjX2RhdGEsIGN0RE5BLk1SRCAlaW4lIGMoIk5FR0FUSVZFIiwgIlBPU0lUSVZFIikpCmNpcmNfZGF0YSROQUMgPC0gZmFjdG9yKGNpcmNfZGF0YSROQUMsIGxldmVscz1jKCJGQUxTRSIsIlRSVUUiKSkKcG9zaXRpdmVfY291bnRzX2J5X3N0YWdlIDwtIGFnZ3JlZ2F0ZShjaXJjX2RhdGEkY3RETkEuTVJEID09ICJQT1NJVElWRSIsIGJ5PWxpc3QoY2lyY19kYXRhJE5BQyksIEZVTj1zdW0pCnRvdGFsX2NvdW50c19ieV9zdGFnZSA8LSBhZ2dyZWdhdGUoY2lyY19kYXRhJGN0RE5BLk1SRCwgYnk9bGlzdChjaXJjX2RhdGEkTkFDKSwgRlVOPWxlbmd0aCkKY29tYmluZWRfZGF0YSA8LSBkYXRhLmZyYW1lKAogIFN0YWdlID0gdG90YWxfY291bnRzX2J5X3N0YWdlJEdyb3VwLjEsCiAgVG90YWxfQ291bnQgPSB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCwKICBQb3NpdGl2ZV9Db3VudCA9IHBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSR4LAogIFJhdGUgPSAocG9zaXRpdmVfY291bnRzX2J5X3N0YWdlJHggLyB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCikKY29tYmluZWRfZGF0YSRSYXRlIDwtIHNwcmludGYoIiUuMmYlJSIsIGNvbWJpbmVkX2RhdGEkUmF0ZSkKb3ZlcmFsbF90b3RhbF9jb3VudCA8LSBucm93KGNpcmNfZGF0YSkKb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCA8LSBucm93KGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuTVJEID09ICJQT1NJVElWRSIsXSkKb3ZlcmFsbF9wb3NpdGl2aXR5X3JhdGUgPC0gKG92ZXJhbGxfcG9zaXRpdmVfY291bnQgLyBvdmVyYWxsX3RvdGFsX2NvdW50KSAqIDEwMCAgIyBDb252ZXJ0IHRvIHBlcmNlbnRhZ2UKb3ZlcmFsbF9yb3cgPC0gZGF0YS5mcmFtZSgKICBTdGFnZSA9ICJPdmVyYWxsIiwKICBUb3RhbF9Db3VudCA9IG92ZXJhbGxfdG90YWxfY291bnQsCiAgUG9zaXRpdmVfQ291bnQgPSBvdmVyYWxsX3Bvc2l0aXZlX2NvdW50LAogIFJhdGUgPSBzcHJpbnRmKCIlLjJmJSUiLCBvdmVyYWxsX3Bvc2l0aXZpdHlfcmF0ZSkKKQpjb21iaW5lZF9kYXRhIDwtIHJiaW5kKGNvbWJpbmVkX2RhdGEsIG92ZXJhbGxfcm93KQpwcmludChjb21iaW5lZF9kYXRhKQoKI2N0RE5BIGF0IFN1cnZlaWxsYW5jZSBieSBTdGFnZQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLlN1cnZlaWxsYW5jZSwgbGV2ZWxzPWMoIk5FR0FUSVZFIiwiUE9TSVRJVkUiKSkKY2lyY19kYXRhJFN0YWdlIDwtIGZhY3RvcihjaXJjX2RhdGEkU3RhZ2UsIGxldmVscz1jKCIwL0kiLCJJSSIsIklJSSIpKQpwb3NpdGl2ZV9jb3VudHNfYnlfc3RhZ2UgPC0gYWdncmVnYXRlKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UgPT0gIlBPU0lUSVZFIiwgYnk9bGlzdChjaXJjX2RhdGEkU3RhZ2UpLCBGVU49c3VtKQp0b3RhbF9jb3VudHNfYnlfc3RhZ2UgPC0gYWdncmVnYXRlKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGJ5PWxpc3QoY2lyY19kYXRhJFN0YWdlKSwgRlVOPWxlbmd0aCkKY29tYmluZWRfZGF0YSA8LSBkYXRhLmZyYW1lKAogIFN0YWdlID0gdG90YWxfY291bnRzX2J5X3N0YWdlJEdyb3VwLjEsCiAgVG90YWxfQ291bnQgPSB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCwKICBQb3NpdGl2ZV9Db3VudCA9IHBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSR4LAogIFJhdGUgPSAocG9zaXRpdmVfY291bnRzX2J5X3N0YWdlJHggLyB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCikKY29tYmluZWRfZGF0YSRSYXRlIDwtIHNwcmludGYoIiUuMmYlJSIsIGNvbWJpbmVkX2RhdGEkUmF0ZSkKb3ZlcmFsbF90b3RhbF9jb3VudCA8LSBucm93KGNpcmNfZGF0YSkKb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCA8LSBucm93KGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlID09ICJQT1NJVElWRSIsXSkKb3ZlcmFsbF9wb3NpdGl2aXR5X3JhdGUgPC0gKG92ZXJhbGxfcG9zaXRpdmVfY291bnQgLyBvdmVyYWxsX3RvdGFsX2NvdW50KSAqIDEwMCAgIyBDb252ZXJ0IHRvIHBlcmNlbnRhZ2UKb3ZlcmFsbF9yb3cgPC0gZGF0YS5mcmFtZSgKICBTdGFnZSA9ICJPdmVyYWxsIiwKICBUb3RhbF9Db3VudCA9IG92ZXJhbGxfdG90YWxfY291bnQsCiAgUG9zaXRpdmVfQ291bnQgPSBvdmVyYWxsX3Bvc2l0aXZlX2NvdW50LAogIFJhdGUgPSBzcHJpbnRmKCIlLjJmJSUiLCBvdmVyYWxsX3Bvc2l0aXZpdHlfcmF0ZSkKKQpjb21iaW5lZF9kYXRhIDwtIHJiaW5kKGNvbWJpbmVkX2RhdGEsIG92ZXJhbGxfcm93KQpwcmludChjb21iaW5lZF9kYXRhKQoKI2N0RE5BIGF0IGFueXRpbWUgYnkgU3RhZ2UKcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIlJXRSBVVFVDX0NsaW5pY2FsIERhdGEgMTAyMDI1LmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJEZpbmFsLmNvaG9ydD09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYW55dGltZSE9IiIsXQpjaXJjX2RhdGEkY3RETkEuYW55dGltZSA8LSBmYWN0b3IoY2lyY19kYXRhJGN0RE5BLmFueXRpbWUsIGxldmVscz1jKCJORUdBVElWRSIsIlBPU0lUSVZFIikpCmNpcmNfZGF0YSRTdGFnZSA8LSBmYWN0b3IoY2lyY19kYXRhJFN0YWdlLCBsZXZlbHM9YygiMC9JIiwiSUkiLCJJSUkiKSkKcG9zaXRpdmVfY291bnRzX2J5X3N0YWdlIDwtIGFnZ3JlZ2F0ZShjaXJjX2RhdGEkY3RETkEuYW55dGltZSA9PSAiUE9TSVRJVkUiLCBieT1saXN0KGNpcmNfZGF0YSRTdGFnZSksIEZVTj1zdW0pCnRvdGFsX2NvdW50c19ieV9zdGFnZSA8LSBhZ2dyZWdhdGUoY2lyY19kYXRhJGN0RE5BLmFueXRpbWUsIGJ5PWxpc3QoY2lyY19kYXRhJFN0YWdlKSwgRlVOPWxlbmd0aCkKY29tYmluZWRfZGF0YSA8LSBkYXRhLmZyYW1lKAogIFN0YWdlID0gdG90YWxfY291bnRzX2J5X3N0YWdlJEdyb3VwLjEsCiAgVG90YWxfQ291bnQgPSB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCwKICBQb3NpdGl2ZV9Db3VudCA9IHBvc2l0aXZlX2NvdW50c19ieV9zdGFnZSR4LAogIFJhdGUgPSAocG9zaXRpdmVfY291bnRzX2J5X3N0YWdlJHggLyB0b3RhbF9jb3VudHNfYnlfc3RhZ2UkeCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCikKY29tYmluZWRfZGF0YSRSYXRlIDwtIHNwcmludGYoIiUuMmYlJSIsIGNvbWJpbmVkX2RhdGEkUmF0ZSkKb3ZlcmFsbF90b3RhbF9jb3VudCA8LSBucm93KGNpcmNfZGF0YSkKb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCA8LSBucm93KGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuYW55dGltZSA9PSAiUE9TSVRJVkUiLF0pCm92ZXJhbGxfcG9zaXRpdml0eV9yYXRlIDwtIChvdmVyYWxsX3Bvc2l0aXZlX2NvdW50IC8gb3ZlcmFsbF90b3RhbF9jb3VudCkgKiAxMDAgICMgQ29udmVydCB0byBwZXJjZW50YWdlCm92ZXJhbGxfcm93IDwtIGRhdGEuZnJhbWUoCiAgU3RhZ2UgPSAiT3ZlcmFsbCIsCiAgVG90YWxfQ291bnQgPSBvdmVyYWxsX3RvdGFsX2NvdW50LAogIFBvc2l0aXZlX0NvdW50ID0gb3ZlcmFsbF9wb3NpdGl2ZV9jb3VudCwKICBSYXRlID0gc3ByaW50ZigiJS4yZiUlIiwgb3ZlcmFsbF9wb3NpdGl2aXR5X3JhdGUpCikKY29tYmluZWRfZGF0YSA8LSByYmluZChjb21iaW5lZF9kYXRhLCBvdmVyYWxsX3JvdykKcHJpbnQoY29tYmluZWRfZGF0YSkKYGBgCgoKCiNEZW1vZ3JhcGhpY3MgVGFibGUKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQoKY2lyY19kYXRhX3N1YnNldCA8LSBjaXJjX2RhdGEgJT4lCiAgc2VsZWN0KAogICAgQWdlLAogICAgU2V4LAogICAgU3RhZ2UsCiAgICBOQUMsCiAgICBBQ1QsCiAgICBBQ1QudHlwZSwKICAgIFJGUy5FdmVudCwKICAgIE9TLkV2ZW50LAogICAgT1MubW9udGhzKSAlPiUKICBtdXRhdGUoCiAgICBBZ2UgPSBhcy5udW1lcmljKEFnZSksCiAgICBTZXggPSBmYWN0b3IoU2V4KSwKICAgIFN0YWdlID0gZmFjdG9yKFN0YWdlKSwKICAgIE5BQyA9IGZhY3RvcihOQUMpLAogICAgQUNUID0gZmFjdG9yKEFDVCksCiAgICBBQ1QudHlwZSA9IGZhY3RvcihBQ1QudHlwZSksCiAgICBSRlMuRXZlbnQgPSBmYWN0b3IoUkZTLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIk5vIFJlY3VycmVuY2UiLCAiUmVjdXJyZW5jZSIpKSwKICAgIE9TLkV2ZW50ID0gZmFjdG9yKE9TLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIkFsaXZlIiwgIkRlY2Vhc2VkIikpLAogICAgT1MubW9udGhzID0gYXMubnVtZXJpYyhPUy5tb250aHMpKSAKdGFibGUxIDwtIGNpcmNfZGF0YV9zdWJzZXQgJT4lCiAgdGJsX3N1bW1hcnkoCiAgICBzdGF0aXN0aWMgPSBsaXN0KAogICAgICBhbGxfY29udGludW91cygpIH4gInttZWRpYW59ICh7bWlufSAtIHttYXh9KSIsCiAgICAgIGFsbF9jYXRlZ29yaWNhbCgpIH4gIntufSAoe3B9JSkiKSkgJT4lCiAgYm9sZF9sYWJlbHMoKQp0YWJsZTEKZml0MSA8LSBhc19mbGV4X3RhYmxlKAogIHRhYmxlMSwKICBpbmNsdWRlID0gZXZlcnl0aGluZygpLAogIHJldHVybl9jYWxscyA9IEZBTFNFKQpmaXQxCnNhdmVfYXNfZG9jeChmaXQxLCBwYXRoPSAifi9Eb3dubG9hZHMvMS4gRGVtb2dyYXBoaWNzIFRhYmxlX1JXRSBVVFVDLmRvY3giKQpgYGAKCgoKI092ZXJ2aWV3IHBsb3QKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNsaW5zdGFnZTwtIHJlYWQuY3N2KCJSV0UgVVRVQ19PUCAxMDIwMjUuY3N2IikKY2xpbnN0YWdlIDwtIGNsaW5zdGFnZVtjbGluc3RhZ2UkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjbGluc3RhZ2VfZGY8LSBhcy5kYXRhLmZyYW1lKGNsaW5zdGFnZSkKCiNEaXNwbGF5IHRoZSBzd2ltbWVyIHBsb3Qgd2l0aCB0aGUgbGFiZWwgYm94Cm9wbG90PC1zd2ltbWVyX3Bsb3QoZGY9Y2xpbnN0YWdlX2RmLAogICAgICAgICAgICAgICAgICAgIGlkPSdQYXRpZW50TmFtZScsCiAgICAgICAgICAgICAgICAgICAgZW5kPSdmdS5kaWZmLm1vbnRocycsCiAgICAgICAgICAgICAgICAgICAgZmlsbD0nZ3JheScsCiAgICAgICAgICAgICAgICAgICAgd2lkdGg9LjAxLCkKb3Bsb3QgPC0gb3Bsb3QgKyB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpCm9wbG90IDwtIG9wbG90ICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA5NiwgYnkgPSAzKSkKb3Bsb3QgPC0gb3Bsb3QgKyBsYWJzKHggPSJQYXRpZW50cyIgLCB5PSJUaW1lIGZyb20gU3VyZ2VyeSAoTW9udGhzKSIpCm9wbG90CgoKIyNwbG90IGV2ZW50cwpvcGxvdF9ldjEgPC0gb3Bsb3QgKyBzd2ltbWVyX3BvaW50cyhkZl9wb2ludHM9Y2xpbnN0YWdlX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZD0nUGF0aWVudE5hbWUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lPSdkYXRlLmRpZmYubW9udGhzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZV9zaGFwZSA9J0V2ZW50X3R5cGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lX2NvbCA9ICdFdmVudCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9My41LGZpbGw9J2JsYWNrJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2NvbD0nZGFya2dyZWVuJwopCm9wbG90X2V2MQoKI1NoYXBlIGN1c3RvbWl6YXRpb24gdG8gRXZlbnRfdHlwZQoKb3Bsb3RfZXYxLjEgPC0gb3Bsb3RfZXYxICsgZ2dwbG90Mjo6c2NhbGVfc2hhcGVfbWFudWFsKG5hbWU9IkV2ZW50X3R5cGUiLHZhbHVlcz1jKDEsMTYsNiwxOCw0KSxicmVha3M9YygnY3RETkFfbmVnJywnY3RETkFfcG9zJywnSW1hZ2luZycsJ1N1cmdlcnknLCdEZWF0aCcpKQpvcGxvdF9ldjEuMQoKI3Bsb3QgdHJlYXRtZW50CgpvcGxvdF9ldjIgPC0gb3Bsb3RfZXYxLjEgKyBzd2ltbWVyX2xpbmVzKGRmX2xpbmVzPWNsaW5zdGFnZV9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZD0nUGF0aWVudE5hbWUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0PSdUeF9zdGFydC5tb250aHMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZD0nVHhfZW5kLm1vbnRocycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZV9jb2w9J1R4X3R5cGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9My41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVfYWxwaGEgPSAxLjApCm9wbG90X2V2MiA8LSBvcGxvdF9ldjIgKyBndWlkZXMobGluZXR5cGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNSwgY29sb3IgPSAiYmxhY2siKSkpCm9wbG90X2V2MiAgCgojY29sb3VyIGN1c3RvbWl6YXRpb24Kb3Bsb3RfZXYyLjIgPC0gb3Bsb3RfZXYyICsgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkV2ZW50Iix2YWx1ZXM9YyggImxpZ2h0Ymx1ZSIsInB1cnBsZSIsImdyZWVuIiwgImJsYWNrIiwgImJsYWNrIiwgImJsYWNrIiwgImxpZ2h0Ymx1ZSIsImxpZ2h0Ymx1ZSIsImdyZWVuIiwicmVkIiwiZ3JlZW4iLCJncmVlbiIsImJsdWUiLCJsaWdodGJsdWUiLCAiYmx1ZSIsICJibHVlIikpCm9wbG90X2V2Mi4yCmBgYAoKI1JGUyBieSBjdEROQSBzdGF0dXMgYXQgTVJEIFdpbmRvdwpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKSAKY2lyY19kYXRhIDwtIHJlYWQuY3N2KCJSV0UgVVRVQ19DbGluaWNhbCBEYXRhIDEwMjAyNS5jc3YiKQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRGaW5hbC5jb2hvcnQ9PSJUUlVFIixdCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJGN0RE5BLk1SRCE9IiIsXQpjaXJjX2RhdGEkUkZTLk1SRCA8LSBjaXJjX2RhdGEkUkZTIC0gY2lyY19kYXRhJGN0RE5BLnRpbWluZwpjaXJjX2RhdGEkUkZTLk1SRC5tb250aHMgPC0gY2lyY19kYXRhJFJGUy5NUkQgLyAzMC40MzcKCnN1cnZmaXQoU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5NUkQubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpfmN0RE5BLk1SRCwgZGF0YSA9IGNpcmNfZGF0YSkKZXZlbnRfc3VtbWFyeSA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY3RETkEuTVJEKSAlPiUKICBzdW1tYXJpc2UoCiAgICBUb3RhbCA9IG4oKSwKICAgIEV2ZW50cyA9IHN1bShSRlMuRXZlbnQpLAogICAgRnJhY3Rpb24gPSBFdmVudHMgLyBuKCksCiAgICBQZXJjZW50YWdlID0gKEV2ZW50cyAvIG4oKSkgKiAxMDAKICApCnByaW50KGV2ZW50X3N1bW1hcnkpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5NUkQubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpCktNX2N1cnZlIDwtIHN1cnZmaXQoc3Vydl9vYmplY3QgfiBjdEROQS5NUkQsIGRhdGEgPSBjaXJjX2RhdGEsY29uZi5pbnQ9MC45NSxjb25mLnR5cGU9ImxvZy1sb2ciKSAKZ2dzdXJ2cGxvdChLTV9jdXJ2ZSwgZGF0YSA9IGNpcmNfZGF0YSwgcHZhbCA9IEZBTFNFLCBjb25mLmludCA9IEZBTFNFLCByaXNrLnRhYmxlID0gVFJVRSwgYnJlYWsudGltZS5ieT0zLCBwYWxldHRlPWMoImJsdWUiLCJyZWQiKSwgdGl0bGU9IlJGUyAtIGN0RE5BIGF0IHRoZSBNUkQgV2luZG93IiwgeWxhYj0gIlJlY3VycmVuY2UtRnJlZSBTdXJ2aXZhbCIsIHhsYWI9IlRpbWUgZnJvbSBMYW5kbWFyayBUaW1lIHBvaW50IChtb250aHMpIiwgbGVnZW5kLmxhYnM9YygiY3RETkEgTmVnYXRpdmUiLCAiY3RETkEgUG9zaXRpdmUiKSwgbGVnZW5kLnRpdGxlPSIiKQpzdW1tYXJ5KEtNX2N1cnZlLCB0aW1lcz0gYygwLCAxMiwgMTgpKQpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHM9YygiTkVHQVRJVkUiLCJQT1NJVElWRSIpKQpjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEuTVJELCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsZGF0YSA9IGNpcmNfZGF0YSkgCnN1bW1hcnkoY294X2ZpdCkKY294X2ZpdF9zdW1tYXJ5IDwtIHN1bW1hcnkoY294X2ZpdCkKCiNFeHRyYWN0IHZhbHVlcyBmb3IgSFIsIDk1JSBDSSwgYW5kIHAtdmFsdWUKSFIgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1syXQpsb3dlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbM10KdXBwZXJfQ0kgPC0gY294X2ZpdF9zdW1tYXJ5JGNvbmYuaW50WzRdCnBfdmFsdWUgPC0gY294X2ZpdF9zdW1tYXJ5JGNvZWZmaWNpZW50c1s1XQpsYWJlbF90ZXh0IDwtIHBhc3RlMCgiSFIgPSAiLCByb3VuZChIUiwgMiksICIgKCIsIHJvdW5kKGxvd2VyX0NJLCAyKSwgIi0iLCByb3VuZCh1cHBlcl9DSSwgMiksICIpOyBwID0gIiwgcm91bmQocF92YWx1ZSwgMykpCnByaW50KGxhYmVsX3RleHQpCgpjaXJjX2RhdGEkY3RETkEuTVJEIDwtIGZhY3RvcihjaXJjX2RhdGEkY3RETkEuTVJELCBsZXZlbHMgPSBjKCJORUdBVElWRSIsICJQT1NJVElWRSIpLCBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpKQpjaXJjX2RhdGEkUkZTLkV2ZW50IDwtIGZhY3RvcihjaXJjX2RhdGEkUkZTLkV2ZW50LCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIiksIGxhYmVscyA9IGMoIk5vIFJlY3VycmVuY2UiLCAiUmVjdXJyZW5jZSIpKQpjb250aW5nZW5jeV90YWJsZSA8LSB0YWJsZShjaXJjX2RhdGEkY3RETkEuTVJELCBjaXJjX2RhdGEkUkZTLkV2ZW50KQpjaGlfc3F1YXJlX3Rlc3QgPC0gY2hpc3EudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoY2hpX3NxdWFyZV90ZXN0KQpmaXNoZXJfZXhhY3RfdGVzdCA8LSBmaXNoZXIudGVzdChjb250aW5nZW5jeV90YWJsZSkKcHJpbnQoZmlzaGVyX2V4YWN0X3Rlc3QpCnByaW50KGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiA8LSBhcy5kYXRhLmZyYW1lKGNvbnRpbmdlbmN5X3RhYmxlKQp0YWJsZV9kZiRUb3RhbCA8LSBhdmUodGFibGVfZGYkRnJlcSwgdGFibGVfZGYkVmFyMSwgRlVOID0gc3VtKQp0YWJsZV9kZiRQZXJjZW50YWdlIDwtIHRhYmxlX2RmJEZyZXEgLyB0YWJsZV9kZiRUb3RhbAp0YWJsZV9kZiRNaWRkbGVQZXJjZW50YWdlIDwtIHRhYmxlX2RmJFBlcmNlbnRhZ2UgLyAyCmdncGxvdCh0YWJsZV9kZiwgYWVzKHggPSBWYXIxLCB5ID0gUGVyY2VudGFnZSwgZmlsbCA9IFZhcjIpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHkgPSBNaWRkbGVQZXJjZW50YWdlLCBsYWJlbCA9IEZyZXEpLCBwb3NpdGlvbiA9ICJzdGFjayIsIGNvbG9yID0gImJsYWNrIiwgdmp1c3QgPSAxLjUsIHNpemUgPSA3KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gImN0RE5BIHN0YXR1cyBhdCBNUkQiLCAKICAgICAgIHggPSAiY3RETkEiLCAKICAgICAgIHkgPSAiUGF0aWVudHMgKCUpIiwgCiAgICAgICBmaWxsID0gIlJlY3VycmVuY2UiLAogICAgICAgY2FwdGlvbiA9IHBhc3RlKCJGaXNoZXIncyBleGFjdCB0ZXN0IHAtdmFsdWU6ICIsIGZvcm1hdC5wdmFsKGZpc2hlcl9leGFjdF90ZXN0JHAudmFsdWUpKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJObyBSZWN1cnJlbmNlIiA9ICJibHVlIiwgIlJlY3VycmVuY2UiID0gInJlZCIpKSArICMgZGVmaW5lIGN1c3RvbSBjb2xvcnMKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMS41LCBzaXplID0gMTQpLCAjIGluY3JlYXNlIHgtYXhpcyB0ZXh0IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeC1heGlzIGxhYmVsIHNpemUKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBjb2xvciA9ICJibGFjayIpLCAjIGluY3JlYXNlIHktYXhpcyBsYWJlbCBzaXplCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBjb2xvciA9ICJibGFjayIpKSAgIyBpbmNyZWFzZSBSZWN1cnJlbmNlIGxhYmVsIHNpemUKYGBgCgojUkZTIGJ5IGN0RE5BIHN0YXR1cyBhdCBTdXJ2ZWlsbGFuY2UgV2luZG93CmBgYHtyfQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpIApjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIlJXRSBVVFVDX0NsaW5pY2FsIERhdGEgMTAyMDI1LmNzdiIpCmNpcmNfZGF0YSA8LSBjaXJjX2RhdGFbY2lyY19kYXRhJEZpbmFsLmNvaG9ydD09IlRSVUUiLF0KY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkY3RETkEuU3VydmVpbGxhbmNlIT0iIixdCgpzdXJ2Zml0KFN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpfmN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSkKZXZlbnRfc3VtbWFyeSA8LSBjaXJjX2RhdGEgJT4lCiAgZ3JvdXBfYnkoY3RETkEuU3VydmVpbGxhbmNlKSAlPiUKICBzdW1tYXJpc2UoCiAgICBUb3RhbCA9IG4oKSwKICAgIEV2ZW50cyA9IHN1bShSRlMuRXZlbnQpLAogICAgRnJhY3Rpb24gPSBFdmVudHMgLyBuKCksCiAgICBQZXJjZW50YWdlID0gKEV2ZW50cyAvIG4oKSkgKiAxMDAKICApCnByaW50KGV2ZW50X3N1bW1hcnkpCnN1cnZfb2JqZWN0IDwtU3Vydih0aW1lID0gY2lyY19kYXRhJFJGUy5tb250aHMsIGV2ZW50ID0gY2lyY19kYXRhJFJGUy5FdmVudCkKS01fY3VydmUgPC0gc3VydmZpdChzdXJ2X29iamVjdCB+IGN0RE5BLlN1cnZlaWxsYW5jZSwgZGF0YSA9IGNpcmNfZGF0YSxjb25mLmludD0wLjk1LGNvbmYudHlwZT0ibG9nLWxvZyIpIApnZ3N1cnZwbG90KEtNX2N1cnZlLCBkYXRhID0gY2lyY19kYXRhLCBwdmFsID0gRkFMU0UsIGNvbmYuaW50ID0gRkFMU0UsIHJpc2sudGFibGUgPSBUUlVFLCBicmVhay50aW1lLmJ5PTYsIHBhbGV0dGU9YygiYmx1ZSIsInJlZCIpLCB0aXRsZT0iUkZTIC0gY3RETkEgYXQgdGhlIFN1cnZlaWxsYW5jZSBXaW5kb3ciLCB5bGFiPSAiUmVjdXJyZW5jZS1GcmVlIFN1cnZpdmFsIiwgeGxhYj0iVGltZSBmcm9tIFN1cmdlcnkgKG1vbnRocykiLCBsZWdlbmQubGFicz1jKCJjdEROQSBOZWdhdGl2ZSIsICJjdEROQSBQb3NpdGl2ZSIpLCBsZWdlbmQudGl0bGU9IiIpCnN1bW1hcnkoS01fY3VydmUsIHRpbWVzPSBjKDAsIDEyLCAyNCkpCmNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGxldmVscz1jKCJORUdBVElWRSIsIlBPU0lUSVZFIikpCmNveF9maXQgPC0gY294cGgoc3Vydl9vYmplY3QgfiBjdEROQS5TdXJ2ZWlsbGFuY2UsIGRhdGE9Y2lyY19kYXRhKSAKZ2dmb3Jlc3QoY294X2ZpdCxkYXRhID0gY2lyY19kYXRhKSAKc3VtbWFyeShjb3hfZml0KQpjb3hfZml0X3N1bW1hcnkgPC0gc3VtbWFyeShjb3hfZml0KQoKI0V4dHJhY3QgdmFsdWVzIGZvciBIUiwgOTUlIENJLCBhbmQgcC12YWx1ZQpIUiA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzJdCmxvd2VyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFszXQp1cHBlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbNF0KcF92YWx1ZSA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzVdCmxhYmVsX3RleHQgPC0gcGFzdGUwKCJIUiA9ICIsIHJvdW5kKEhSLCAyKSwgIiAoIiwgcm91bmQobG93ZXJfQ0ksIDIpLCAiLSIsIHJvdW5kKHVwcGVyX0NJLCAyKSwgIik7IHAgPSAiLCByb3VuZChwX3ZhbHVlLCAzKSkKcHJpbnQobGFiZWxfdGV4dCkKCmNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGxldmVscyA9IGMoIk5FR0FUSVZFIiwgIlBPU0lUSVZFIiksIGxhYmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpCmNpcmNfZGF0YSRSRlMuRXZlbnQgPC0gZmFjdG9yKGNpcmNfZGF0YSRSRlMuRXZlbnQsIGxldmVscyA9IGMoIkZBTFNFIiwgIlRSVUUiKSwgbGFiZWxzID0gYygiTm8gUmVjdXJyZW5jZSIsICJSZWN1cnJlbmNlIikpCmNvbnRpbmdlbmN5X3RhYmxlIDwtIHRhYmxlKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGNpcmNfZGF0YSRSRlMuRXZlbnQpCmNoaV9zcXVhcmVfdGVzdCA8LSBjaGlzcS50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChjaGlfc3F1YXJlX3Rlc3QpCmZpc2hlcl9leGFjdF90ZXN0IDwtIGZpc2hlci50ZXN0KGNvbnRpbmdlbmN5X3RhYmxlKQpwcmludChmaXNoZXJfZXhhY3RfdGVzdCkKcHJpbnQoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29udGluZ2VuY3lfdGFibGUpCnRhYmxlX2RmJFRvdGFsIDwtIGF2ZSh0YWJsZV9kZiRGcmVxLCB0YWJsZV9kZiRWYXIxLCBGVU4gPSBzdW0pCnRhYmxlX2RmJFBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkRnJlcSAvIHRhYmxlX2RmJFRvdGFsCnRhYmxlX2RmJE1pZGRsZVBlcmNlbnRhZ2UgPC0gdGFibGVfZGYkUGVyY2VudGFnZSAvIDIKZ2dwbG90KHRhYmxlX2RmLCBhZXMoeCA9IFZhcjEsIHkgPSBQZXJjZW50YWdlLCBmaWxsID0gVmFyMikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fdGV4dChhZXMoeSA9IE1pZGRsZVBlcmNlbnRhZ2UsIGxhYmVsID0gRnJlcSksIHBvc2l0aW9uID0gInN0YWNrIiwgY29sb3IgPSAiYmxhY2siLCB2anVzdCA9IDEuNSwgc2l6ZSA9IDcpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiY3RETkEgc3RhdHVzIGF0IFN1cnZlaWxsYW5jZSIsIAogICAgICAgeCA9ICJjdEROQSIsIAogICAgICAgeSA9ICJQYXRpZW50cyAoJSkiLCAKICAgICAgIGZpbGwgPSAiUmVjdXJyZW5jZSIsCiAgICAgICBjYXB0aW9uID0gcGFzdGUoIkZpc2hlcidzIGV4YWN0IHRlc3QgcC12YWx1ZTogIiwgZm9ybWF0LnB2YWwoZmlzaGVyX2V4YWN0X3Rlc3QkcC52YWx1ZSkpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vIFJlY3VycmVuY2UiID0gImJsdWUiLCAiUmVjdXJyZW5jZSIgPSAicmVkIikpICsgIyBkZWZpbmUgY3VzdG9tIGNvbG9ycwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxLjUsIHNpemUgPSAxNCksICMgaW5jcmVhc2UgeC1heGlzIHRleHQgc2l6ZQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB5LWF4aXMgdGV4dCBzaXplCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3IgPSAiYmxhY2siKSwgIyBpbmNyZWFzZSB4LWF4aXMgbGFiZWwgc2l6ZQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGNvbG9yID0gImJsYWNrIiksICMgaW5jcmVhc2UgeS1heGlzIGxhYmVsIHNpemUKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGNvbG9yID0gImJsYWNrIikpICAjIGluY3JlYXNlIFJlY3VycmVuY2UgbGFiZWwgc2l6ZQpgYGAKCiNUaW1lLWRlcGVuZGVudCBhbmFseXNpcyBmb3IgUkZTIGluIGxvbmdpdHVkaW5hbCB0aW1lIHBvaW50cwpgYGB7cn0Kcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpkdCA8LSByZWFkX3hsc3goIkNMSUEgVVRVQyBDbGluaWNhbCBEYXRhX1RpbWUgZGVwZW5kZW50Lnhsc3giKSB8PgogIGNsZWFuX25hbWVzKCkgfD4KICBtdXRhdGUoYWNyb3NzKAogICAgLmNvbHMgPSBjKHdpbmRvd19zdGFydF9kYXRlLCBkZnNfZGF0ZSwgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSksCiAgICAuZm5zID0gfiBhc19kYXRlKGFzLkRhdGUoLngsIGZvcm1hdCA9ICIlWS0lbS0lZCIpKQogICkpIHw+CiAgZmlsdGVyKGZpbmFsX2NvaG9ydCA9PSBUUlVFKQoKZHRfYmlvbWFya2VyIDwtIGR0IHw+CiAgc2VsZWN0KHB0c19pZCwgY3RfZG5hX3N1cnZlaWxsYW5jZV9hdmFpbGFibGUsCiAgICAgICAgIHdpbmRvd19zdGFydF9kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9zdGF0dXM6c3VydmVpbGxhbmNlXzEyX2RhdGUpIHw+CiAgZmlsdGVyKGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlKSB8PgogIHBpdm90X2xvbmdlcihjb2xzID0gc3VydmVpbGxhbmNlXzFfc3RhdHVzOnN1cnZlaWxsYW5jZV8xMl9kYXRlLAogICAgICAgICAgICAgICBuYW1lc190byA9IGMoInZpc2l0X251bWJlciIsICIudmFsdWUiKSwKICAgICAgICAgICAgICAgbmFtZXNfcGF0dGVybiA9ICJzdXJ2ZWlsbGFuY2VfKC4pXyguKikiKSB8PgogIG11dGF0ZShiaW9tYXJrZXJfdGltZSA9IGRheShkYXlzKGRhdGUgLSB3aW5kb3dfc3RhcnRfZGF0ZSkpKSB8PgogIHNlbGVjdChwdHNfaWQsIGJpb21hcmtlcl90aW1lLCBiaW9tYXJrZXJfc3RhdHVzID0gc3RhdHVzKSB8PgogIGZpbHRlcighaXMubmEoYmlvbWFya2VyX3RpbWUpKQoKZ2xpbXBzZShkdF9iaW9tYXJrZXIpCgpkdF9zdXJ2aXZhbCA8LSBkdCB8PgogIHNlbGVjdChwdHNfaWQsIGN0X2RuYV9zdXJ2ZWlsbGFuY2VfYXZhaWxhYmxlLAogICAgICAgICB3aW5kb3dfc3RhcnRfZGF0ZTpkZnNfZGF0ZSwgZGZzX2V2ZW50KSB8PiAgIyBBZGRlZCBkZnNfZXZlbnQgaGVyZQogIGZpbHRlcihjdF9kbmFfc3VydmVpbGxhbmNlX2F2YWlsYWJsZSkgfD4KICBtdXRhdGUoZGZzX3RpbWUgPSAoZGZzX2RhdGUgLSB3aW5kb3dfc3RhcnRfZGF0ZSksCiAgICAgICAgIGRmc190aW1lID0gZGF5KGRheXMoZGZzX3RpbWUpKSwKICAgICAgICAgZGZzX2V2ZW50ID0gYXMubnVtZXJpYyhkZnNfZXZlbnQpKSB8PgogIHNlbGVjdChwdHNfaWQsIGRmc190aW1lLCBkZnNfZXZlbnQpCgpnbGltcHNlKGR0X3N1cnZpdmFsKQoKYXV4IDwtIGR0X3N1cnZpdmFsICU+JSAKICBmaWx0ZXIoZGZzX3RpbWUgPD0gMCkKCnRhYiA8LSBsZWZ0X2pvaW4oYXV4LCBkdCkgfD4KICBzZWxlY3QocHRzX2lkLCB3aW5kb3dfc3RhcnRfZGF0ZSwgZGZzX3RpbWUsIGRmc19kYXRlLAogICAgICAgICBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBkZnNfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiBhc19kYXRlKC54KSkpIHw+CiAgc2VsZWN0KHB0c19pZCwgd2luZG93X3N0YXJ0X2RhdGUsIGRmc19kYXRlLCBkZnNfdGltZSkKCmRhdGF0YWJsZSh0YWIsIGZpbHRlciA9ICJ0b3AiKQoKZHRfc3Vydml2YWwgPC0gZHRfc3Vydml2YWwgfD4KICBmaWx0ZXIoZGZzX3RpbWUgPiAwKQoKYXV4IDwtIGR0IHw+CiAgc2VsZWN0KHB0c19pZCwgY3RfZG5hX3N1cnZlaWxsYW5jZV9hdmFpbGFibGUsCiAgICAgICAgIHdpbmRvd19zdGFydF9kYXRlLCBkZnNfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiAueCAtIHdpbmRvd19zdGFydF9kYXRlKSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSwgCiAgICAgICAgICAgICAgICAuZm5zID0gfiAueCA8IDApKSB8PgogIHJvd3dpc2UoKSB8PgogIG11dGF0ZShzdW1fbmVnID0gCiAgICAgICAgICAgc3VtKGNfYWNyb3NzKHN1cnZlaWxsYW5jZV8xX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUpLAogICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKSAgfD4KICBzZWxlY3QocHRzX2lkLCBzdW1fbmVnKQoKdGFiIDwtIGxlZnRfam9pbihhdXgsIGR0KSB8PgogIGZpbHRlcihzdW1fbmVnID4gMCkgfD4KICBzZWxlY3QocHRzX2lkLCBzdW1fbmVnLCB3aW5kb3dfc3RhcnRfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gd2luZG93X3N0YXJ0X2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gYXNfZGF0ZSgueCkpKSAKCmRhdGF0YWJsZSh0YWIsIGZpbHRlciA9ICJ0b3AiKQoKYXV4IDwtIGR0IHw+CiAgc2VsZWN0KHB0c19pZCwgY3RfZG5hX3N1cnZlaWxsYW5jZV9hdmFpbGFibGUsCiAgICAgICAgIHdpbmRvd19zdGFydF9kYXRlLCBkZnNfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzFfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gZGZzX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gLnggLSB3aW5kb3dfc3RhcnRfZGF0ZSkpIHw+CiAgbXV0YXRlKGFjcm9zcyguY29scyA9IHN1cnZlaWxsYW5jZV8yX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsCiAgICAgICAgICAgICAgICAuZm5zID0gfiBkZnNfZGF0ZSA8IC54KSkgfD4KICByb3d3aXNlKCkgfD4KICBtdXRhdGUobl9iaW9tYXJrZXJfYWZ0ZXJfZXZlbnQgPSBzdW0oY19hY3Jvc3Moc3VydmVpbGxhbmNlXzJfZGF0ZToKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKSB8PgogIG11dGF0ZShhY3Jvc3MoLmNvbHMgPSBzdXJ2ZWlsbGFuY2VfMV9kYXRlOnN1cnZlaWxsYW5jZV8xMl9kYXRlLAogICAgICAgICAgICAgICAgLmZucyA9IH4gIWlzLm5hKC54KSkpIHw+CiAgbXV0YXRlKHRvdGFsX2Jpb21hcmtlciA9IHN1bShjX2Fjcm9zcyhzdXJ2ZWlsbGFuY2VfMl9kYXRlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSkgfD4KICBzZWxlY3QocHRzX2lkLCBuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCwgdG90YWxfYmlvbWFya2VyKQoKdGVtcCA8LSBhdXggfD4gCiAgc2VsZWN0KC1wdHNfaWQpIHw+IAogIGdyb3VwX2J5KG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50LCB0b3RhbF9iaW9tYXJrZXIpIHw+ICAjIERpcmVjdCBncm91cGluZwogIHN1bW1hcmlzZShmcmVxID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKSAgIyBEcm9wIGdyb3VwcyBhZnRlciBzdW1tYXJpemF0aW9uCgoKdGFiIDwtIGxlZnRfam9pbihhdXgsIGR0KSB8PgogIHNlbGVjdChwdHNfaWQsIG5fYmlvbWFya2VyX2FmdGVyX2V2ZW50LCB0b3RhbF9iaW9tYXJrZXIsIAogICAgICAgICBkZnNfZGF0ZSwKICAgICAgICAgc3VydmVpbGxhbmNlXzJfZGF0ZTpzdXJ2ZWlsbGFuY2VfMTJfZGF0ZSkgfD4KICBtdXRhdGUoYWNyb3NzKC5jb2xzID0gZGZzX2RhdGU6c3VydmVpbGxhbmNlXzEyX2RhdGUsIAogICAgICAgICAgICAgICAgLmZucyA9IH4gYXNfZGF0ZSgueCkpKSB8PgogIGZpbHRlcihuX2Jpb21hcmtlcl9hZnRlcl9ldmVudCA+IDApCmRhdGF0YWJsZSh0YWIsIGZpbHRlciA9ICJ0b3AiKQoKYXV4IDwtIHRtZXJnZShkYXRhMSA9IGR0X3N1cnZpdmFsLCAKICAgICAgICAgICAgICBkYXRhMiA9IGR0X3N1cnZpdmFsLAogICAgICAgICAgICAgIGlkID0gcHRzX2lkLCAKICAgICAgICAgICAgICBkZnNfZXZlbnQgPSBldmVudChkZnNfdGltZSwgZGZzX2V2ZW50KSkKZHRfZmluYWwgPC0gdG1lcmdlKGRhdGExID0gYXV4LCAKICAgICAgICAgICAgICAgICAgIGRhdGEyID0gZHRfYmlvbWFya2VyLAogICAgICAgICAgICAgICAgICAgaWQgPSBwdHNfaWQsIAogICAgICAgICAgICAgICAgICAgYmlvbWFya2VyX3N0YXR1cyA9IAogICAgICAgICAgICAgICAgICAgICB0ZGMoYmlvbWFya2VyX3RpbWUsIGJpb21hcmtlcl9zdGF0dXMpKQoKZGF0YXRhYmxlKGR0X2ZpbmFsLCBmaWx0ZXIgPSAidG9wIikKCiMgU3ludGF4IGlmIHRoZXJlIGlzIG5vdCB0aW1lLWRlcGVuZGVudCBjb3ZhcmlhdGUKIyBmaXQgPC0gY294cGgoU3VydihkZnNfdGltZSwgZGZzX2V2ZW50KSB+IGJpb21hcmtlcl9zdGF0dXMsCiMgICAgICAgICAgICAgIGRhdGEgPSBkdF9maW5hbCkKIyBzdW1tYXJ5KGZpdCkKCmZpdCA8LSBjb3hwaChTdXJ2KHRzdGFydCwgdHN0b3AsIGRmc19ldmVudCkgfiBiaW9tYXJrZXJfc3RhdHVzLAogICAgICAgICAgICAgZGF0YSA9IGR0X2ZpbmFsKQpzdW1tYXJ5KGZpdCkKY294X2ZpdF9zdW1tYXJ5IDwtIHN1bW1hcnkoZml0KQoKI0V4dHJhY3QgdmFsdWVzIGZvciBIUiwgOTUlIENJLCBhbmQgcC12YWx1ZQpIUiA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzJdCmxvd2VyX0NJIDwtIGNveF9maXRfc3VtbWFyeSRjb25mLmludFszXQp1cHBlcl9DSSA8LSBjb3hfZml0X3N1bW1hcnkkY29uZi5pbnRbNF0KcF92YWx1ZSA8LSBjb3hfZml0X3N1bW1hcnkkY29lZmZpY2llbnRzWzVdCmxhYmVsX3RleHQgPC0gcGFzdGUwKCJIUiA9ICIsIHJvdW5kKEhSLCAyKSwgIiAoIiwgcm91bmQobG93ZXJfQ0ksIDIpLCAiLSIsIHJvdW5kKHVwcGVyX0NJLCAyKSwgIik7IHAgPSAiLCByb3VuZChwX3ZhbHVlLCAzKSkKcHJpbnQobGFiZWxfdGV4dCkKYGBgCgoKI01lZGlhbiBudW1iZXJzIG9mIHRpbWUgcG9pbnRzIGluIHRoZSBsb25naXR1ZGluYWwgc2V0dGluZwpgYGB7cn0KIyBMb2FkIHRoZSBkYXRhc2V0CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KY2lyY19kYXRhZGYgPC0gYXMuZGF0YS5mcmFtZShjaXJjX2RhdGEpCgptZWRpYW5fTnN1cnZ0cHMgPC0gbWVkaWFuKGNpcmNfZGF0YWRmJE5zdXJ2dHBzLCBuYS5ybSA9IFRSVUUpCm1pbl9Oc3VydnRwcyA8LSBtaW4oY2lyY19kYXRhZGYkTnN1cnZ0cHMsIG5hLnJtID0gVFJVRSkKbWF4X05zdXJ2dHBzIDwtIG1heChjaXJjX2RhdGFkZiROc3VydnRwcywgbmEucm0gPSBUUlVFKQoKY2F0KHNwcmludGYoIk1lZGlhbiAjIG9mIHN1cnZlaWxsYW5jZSB0aW1lIHBvaW50czogJWQgKCVkLSVkKVxuIiwgCiAgICAgICAgICAgIG1lZGlhbl9Oc3VydnRwcywgbWluX05zdXJ2dHBzLCBtYXhfTnN1cnZ0cHMpKQpgYGAKCiNNZWRpYW4gb2YgbWVkaWFuIGludGVydmFsIG9mIGN0RE5BIHRpbWVwb2ludHMgYW5kIHJhZGlvbG9naWNhbCBpbWFnaW5nIGFzc2Vzc21lbnQKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKZGYgPC0gcmVhZC5jc3YoIlJXRSBVVFVDX09QIDEwMjAyNS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgpuYW1lcyhkZikgPC0gdHJpbXdzKG5hbWVzKGRmKSkKCiMgLS0tLSBIZWxwZXI6IGNvbXB1dGUgbWVkaWFuIGludGVydmFsIHBlciBwYXRpZW50IC0tLS0KbWVkaWFuX2ludGVydmFsX3Blcl9wYXRpZW50IDwtIGZ1bmN0aW9uKGRhdGEsIGZpbHRlcl9jb2wsIGZpbHRlcl92YWwpIHsKICBkYXRhICU+JQogICAgZmlsdGVyKCEhc3ltKGZpbHRlcl9jb2wpID09IGZpbHRlcl92YWwpICU+JQogICAgYXJyYW5nZShQYXRpZW50TmFtZSwgZGF0ZS5kaWZmKSAlPiUKICAgIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICAgIG11dGF0ZShpbnRlcnZhbCA9IGRhdGUuZGlmZiAtIGxhZyhkYXRlLmRpZmYpKSAlPiUKICAgIHN1bW1hcmlzZShtZWRpYW5faW50ZXJ2YWwgPSBtZWRpYW4oaW50ZXJ2YWwsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogICAgbXV0YXRlKGV2ZW50X2dyb3VwID0gZmlsdGVyX3ZhbCkKfQoKIyAtLS0tIENvbXB1dGUgcGVyLXBhdGllbnQgbWVkaWFucyAtLS0tCmN0RE5BX3BhdGllbnRfbWVkaWFucyAgIDwtIG1lZGlhbl9pbnRlcnZhbF9wZXJfcGF0aWVudChkZiwgIkV2ZW50IiwgImN0RE5BIikKaW1hZ2luZ19wYXRpZW50X21lZGlhbnMgPC0gbWVkaWFuX2ludGVydmFsX3Blcl9wYXRpZW50KGRmLCAiRXZlbnRfdHlwZSIsICJJbWFnaW5nIikKCiMgLS0tLSBGdW5jdGlvbiB0byBzdW1tYXJpemUgcGF0aWVudC1sZXZlbCBtZWRpYW5zIHRvIGNvaG9ydC1sZXZlbCAtLS0tCmNvaG9ydF9zdW1tYXJ5IDwtIGZ1bmN0aW9uKHBhdGllbnRfZGF0YSwgZXZlbnRfbGFiZWwpIHsKICAjIEZpbHRlciBvdXQgcGF0aWVudHMgd2l0aCBOQSBtZWRpYW4gaW50ZXJ2YWxzCiAgdmFsaWRfZGF0YSA8LSBwYXRpZW50X2RhdGEgJT4lIGZpbHRlcighaXMubmEobWVkaWFuX2ludGVydmFsKSkKICAKICBkYXRhLmZyYW1lKAogICAgRXZlbnRfVHlwZSA9IGV2ZW50X2xhYmVsLAogICAgQ29ob3J0X01lZGlhbl9GcmVxdWVuY3kgPSBtZWRpYW4odmFsaWRfZGF0YSRtZWRpYW5faW50ZXJ2YWwsIG5hLnJtID0gVFJVRSksCiAgICBNaW5fUGF0aWVudF9NZWRpYW4gPSBtaW4odmFsaWRfZGF0YSRtZWRpYW5faW50ZXJ2YWwsIG5hLnJtID0gVFJVRSksCiAgICBNYXhfUGF0aWVudF9NZWRpYW4gPSBtYXgodmFsaWRfZGF0YSRtZWRpYW5faW50ZXJ2YWwsIG5hLnJtID0gVFJVRSksCiAgICBSYW5nZV9QYXRpZW50X01lZGlhbiA9IG1heCh2YWxpZF9kYXRhJG1lZGlhbl9pbnRlcnZhbCwgbmEucm0gPSBUUlVFKSAtIAogICAgICBtaW4odmFsaWRfZGF0YSRtZWRpYW5faW50ZXJ2YWwsIG5hLnJtID0gVFJVRSkKICApCn0KCiMgLS0tLSBDb21iaW5lIGFsbCByZXN1bHRzIC0tLS0KcmVzdWx0cyA8LSBiaW5kX3Jvd3MoCiAgY29ob3J0X3N1bW1hcnkoY3RETkFfcGF0aWVudF9tZWRpYW5zLCAiY3RETkEiKSwKICBjb2hvcnRfc3VtbWFyeShpbWFnaW5nX3BhdGllbnRfbWVkaWFucywgIkltYWdpbmciKQopCgojIC0tLS0gUHJpbnQgY29ob3J0LWxldmVsIHN1bW1hcnkgLS0tLQpjYXQoIj09PT09IE1lZGlhbiBGcmVxdWVuY3kgKERheXMpIHBlciBDb2hvcnQgPT09PT1cbiIpCnByaW50KHJlc3VsdHMsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyAtLS0tIE9QVElPTkFMOiBTYXZlIHBhdGllbnQtbGV2ZWwgbWVkaWFucyAtLS0tCnBhdGllbnRfbGV2ZWxfbWVkaWFucyA8LSBiaW5kX3Jvd3MoY3RETkFfcGF0aWVudF9tZWRpYW5zLCBpbWFnaW5nX3BhdGllbnRfbWVkaWFucykKI3dyaXRlLmNzdihwYXRpZW50X2xldmVsX21lZGlhbnMsICJwYXRpZW50X21lZGlhbl9pbnRlcnZhbHMuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKI1Bsb3QgZm9yIGluZGl2aWR1YWwgbGVhZC10aW1lIGNhbGN1bGF0aW9ucyBmb3IgZWFjaCBwdApgYGB7cn0Kcm0obGlzdD1scygpKQpjc3ZfcGF0aCA8LSAifi9Eb3dubG9hZHMvQ0xJQSBVVFVDX0xlYWQgVGltZSBwdHMuY3N2IgpkZiA8LSByZWFkLmNzdihjc3ZfcGF0aCwgY2hlY2submFtZXMgPSBGQUxTRSkKaWYgKCJGaW5hbC5jb2hvcnQiICVpbiUgbmFtZXMoZGYpKSB7CiAgZGYgPC0gZGZbZGYkRmluYWwuY29ob3J0ID09IFRSVUUsIF0KfSBlbHNlIHsKICB3YXJuaW5nKCJDb2x1bW4gJ0ZpbmFsLmNvaG9ydCcgbm90IGZvdW5kIGluIHRoZSBkYXRhc2V0LiIpCn0KaWRfY2FuZGlkYXRlcyA8LSBjKCJQYXRpZW50IiwiSUQiLCJQdCIsIlN1YmplY3QiLCJTYW1wbGUiKQppZF9jb2wgPC0gaWRfY2FuZGlkYXRlc1tpZF9jYW5kaWRhdGVzICVpbiUgbmFtZXMoZGYpXVsxXQppZiAoaXMubmEoaWRfY29sKSkgewogIGRmIDwtIGRmICU+JSBtdXRhdGUoUGF0aWVudCA9IHJvd19udW1iZXIoKSkKICBpZF9jb2wgPC0gIlBhdGllbnQiCn0KCm51bV9jb2xzIDwtIGludGVyc2VjdChjKCJNUiIsIkNSIiwiTFQiKSwgbmFtZXMoZGYpKQpkZltudW1fY29sc10gPC0gbGFwcGx5KGRmW251bV9jb2xzXSwgZnVuY3Rpb24oeCkgc3VwcHJlc3NXYXJuaW5ncyhhcy5udW1lcmljKHgpKSkKCiMgSWYgTFQgbWlzc2luZywgY29tcHV0ZSBpbiBkYXlzCmlmICghKCJMVCIgJWluJSBuYW1lcyhkZikpKSB7CiAgZGYgPC0gZGYgJT4lIG11dGF0ZShMVCA9IENSIC0gTVIpCn0KCiMgLS0tLSBjb252ZXJ0IHRvIG1vbnRocyAtLS0tCiMgQXBwcm94aW1hdGU6IDMwLjQ0IGRheXMgcGVyIG1vbnRoIChhdmVyYWdlKQpkYXlzX3RvX21vbnRocyA8LSBmdW5jdGlvbih4KSB4IC8gMzAuNDM3CgpkZiA8LSBkZiAlPiUKICBtdXRhdGUoTVJfbW8gPSBkYXlzX3RvX21vbnRocyhNUiksCiAgICAgICAgIENSX21vID0gZGF5c190b19tb250aHMoQ1IpLAogICAgICAgICBMVF9tbyA9IGRheXNfdG9fbW9udGhzKExUKSkKCiMgLS0tLSBvcmRlcmluZyBmb3IgeS1heGlzIC0tLS0KZGYgPC0gZGYgJT4lCiAgbXV0YXRlKEVhcmxpZXN0ID0gcG1pbihNUl9tbywgQ1JfbW8sIG5hLnJtID0gVFJVRSkpICU+JQogIGFycmFuZ2UoRWFybGllc3QpICU+JQogIG11dGF0ZShQYXRpZW50X2YgPSBmYWN0b3IoLmRhdGFbW2lkX2NvbF1dLCBsZXZlbHMgPSAuZGF0YVtbaWRfY29sXV0pKQoKIyAtLS0tIGxvbmcgZm9ybWF0IGZvciBwb2ludHMgLS0tLQpwb2ludHNfbG9uZyA8LSBkZiAlPiUKICBzZWxlY3QoUGF0aWVudF9mLCBNUl9tbywgQ1JfbW8pICU+JQogIHBpdm90X2xvbmdlcihjKE1SX21vLCBDUl9tbyksIG5hbWVzX3RvID0gIlR5cGUiLCB2YWx1ZXNfdG8gPSAiTW9udGhzIikgJT4lCiAgbXV0YXRlKFR5cGUgPSBkcGx5cjo6cmVjb2RlKFR5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNUl9tbyIgPSAiTW9sZWN1bGFyIHJlY3VycmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ1JfbW8iID0gIkNsaW5pY2FsIHJlY3VycmVuY2UiKSkKCiMgLS0tLSBzZWdtZW50cyBiZXR3ZWVuIE1SIGFuZCBDUiAtLS0tCnNlZ21lbnRzX2RmIDwtIGRmICU+JQogIHRyYW5zbXV0ZShQYXRpZW50X2YsCiAgICAgICAgICAgIHgwID0gTVJfbW8sIHgxID0gQ1JfbW8sCiAgICAgICAgICAgIGx0X2ZsYWcgPSBpZl9lbHNlKExUID4gMTIwLCAiZ3QxMjAiLCAibGUxMjAiKSkKCiMgLS0tLSBhbm5vdGF0aW9uIC0tLS0KbWVkX2x0ICA8LSBtZWRpYW4oZGYkTFRfbW8sIG5hLnJtID0gVFJVRSkKbWluX2x0ICA8LSBtaW4oZGYkTFRfbW8sIG5hLnJtID0gVFJVRSkKbWF4X2x0ICA8LSBtYXgoZGYkTFRfbW8sIG5hLnJtID0gVFJVRSkKCmFubm90X2xhYmVsIDwtIHNwcmludGYoIk1lZGlhbiBsZWFkLXRpbWU6XG4lLjFmIG1vbnRocyAoJS4xZiB0byAlLjFmKSIsCiAgICAgICAgICAgICAgICAgICAgICAgbWVkX2x0LCBtaW5fbHQsIG1heF9sdCkKCiMgLS0tLSBwbG90IC0tLS0KcGFsIDwtIGMoIk1vbGVjdWxhciByZWN1cnJlbmNlIiA9ICIjMTBCNEMxIiwKICAgICAgICAgIkNsaW5pY2FsIHJlY3VycmVuY2UiICA9ICIjQzk2QTcyIikKCnhfbWF4IDwtIG1heChjKGRmJE1SX21vLCBkZiRDUl9tbyksIG5hLnJtID0gVFJVRSkKeV9taWQgPC0gbGV2ZWxzKGRmJFBhdGllbnRfZilbY2VpbGluZyhubGV2ZWxzKGRmJFBhdGllbnRfZikgKiAwLjU1KV0KCnAgPC0gZ2dwbG90KCkgKwogIGdlb21fc2VnbWVudChkYXRhID0gc2VnbWVudHNfZGYsCiAgICAgICAgICAgICAgIGFlcyh4ID0geDAsIHhlbmQgPSB4MSwgeSA9IFBhdGllbnRfZiwgeWVuZCA9IFBhdGllbnRfZiwKICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gbHRfZmxhZyksCiAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDAuNiwgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMobGUxMjAgPSAic29saWQiLCBndDEyMCA9ICJkYXNoZWQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSAibm9uZSIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBwb2ludHNfbG9uZywKICAgICAgICAgICAgIGFlcyh4ID0gTW9udGhzLCB5ID0gUGF0aWVudF9mLCBjb2xvciA9IFR5cGUpLAogICAgICAgICAgICAgc2l6ZSA9IDIuOCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwsIG5hbWUgPSBOVUxMKSArCiAgbGFicyh4ID0gIk1vbnRocyBmcm9tIFN1cmdlcnkgb3IgRGVmaW5pdGl2ZSBUcmVhdG1lbnQiLCB5ID0gTlVMTCkgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0geF9tYXggKiAwLjczLAogICAgICAgICAgIHkgPSB5X21pZCwKICAgICAgICAgICBsYWJlbCA9IGFubm90X2xhYmVsLAogICAgICAgICAgIGhqdXN0ID0gMCwgdmp1c3QgPSAwLjUsIHNpemUgPSAzLjgpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIGNlaWxpbmcoeF9tYXgvMykqMywgYnkgPSAzKSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCB4X21heCAqIDEuMDUpKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTIpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTg1IiwgbGluZXdpZHRoID0gMC4zKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTkyIiwgbGluZXdpZHRoID0gMC4yKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KQogICkKCnByaW50KHApCgp3Y19kZiA8LSBkZiAlPiUgZHBseXI6OnNlbGVjdChNUiwgQ1IpICU+JSB0aWR5cjo6ZHJvcF9uYSgpCnBhaXJlZF9kaWZmIDwtIHdjX2RmJENSIC0gd2NfZGYkTVIKCndpbHggPC0gd2lsY294LnRlc3QoCiAgeCA9IHdjX2RmJENSLCB5ID0gd2NfZGYkTVIsCiAgcGFpcmVkID0gVFJVRSwKICBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiLAogIGNvbmYuaW50ID0gVFJVRSwgICAgICAjIGdpdmVzIENJIGZvciB0aGUgSG9kZ2Vz4oCTTGVobWFubiBlc3RpbWF0ZSBvZiB0aGUgbWVkaWFuIGRpZmZlcmVuY2UKICBleGFjdCA9IEZBTFNFICAgICAgICAgIyBzYWZlciBmb3IgdGllcy9sYXJnZSBOCikKCldfc3RhdCAgIDwtIHVubmFtZSh3aWx4JHN0YXRpc3RpYykgICAgICAgICAgIyBXaWxjb3hvbiBWCnBfdmFsdWUgIDwtIHdpbHgkcC52YWx1ZQpITF9lc3QgICA8LSB1bm5hbWUod2lseCRlc3RpbWF0ZSkgICAgICAgICAgICMgbWVkaWFuIG9mIChDUiAtIE1SKQpITF9jaSAgICA8LSB3aWx4JGNvbmYuaW50ICAgICAgICAgICAgICAgICAgICMgQ0kgZm9yIG1lZGlhbiBkaWZmZXJlbmNlCgojIFNpbXBsZSBwLXZhbHVlIGZvcm1hdHRlciBmb3IgYW5ub3RhdGlvbi9wdWJsaWNhdGlvbgpmbXRfcCA8LSBmdW5jdGlvbihwKSB7CiAgaWYgKGlzLm5hKHApKSByZXR1cm4oIk5BIikKICBpZiAocCA8IDAuMDAxKSAiPCAwLjAwMSIgZWxzZSBzcHJpbnRmKCI9ICUuM2YiLCByb3VuZChwLCAzKSkKfQpwX3RleHQgPC0gcGFzdGUwKCJQICIsIGZtdF9wKHBfdmFsdWUpKSAgIyBlLmcuLCAiUCA9IDAuMDAzIiBvciAiUCA8IDAuMDAxIgoKIyBPcHRpb25hbDogcHJpbnQgYSBjb21wYWN0IHN1bW1hcnkKY2F0KCJcblBhaXJlZCBXaWxjb3hvbiBzaWduZWQtcmFuayB0ZXN0IChDUiB2cyBNUilcbiIsCiAgICAiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIiwKICAgIHNwcmludGYoIk4gcGFpcnM6ICVkIiwgbnJvdyh3Y19kZikpLCAiXG4iLAogICAgc3ByaW50ZigiVyAoVik6ICVnIiwgV19zdGF0KSwgIlxuIiwKICAgIHNwcmludGYoIlAtdmFsdWU6ICVzIiwgaWZlbHNlKHBfdmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCBzcHJpbnRmKCIlLjZmIiwgcF92YWx1ZSkpKSwgIlxuIiwKICAgIHNwcmludGYoIkhvZGdlc+KAk0xlaG1hbm4gbWVkaWFuIGRpZmZlcmVuY2UgKENSIC0gTVIpOiAlLjFmIGRheXMiLCBITF9lc3QpLCAiXG4iLAogICAgc3ByaW50ZigiOTUlJSBDSTogWyUuMWYsICUuMWZdIGRheXMiLCBITF9jaVsxXSwgSExfY2lbMl0pLCAiXG4iLCBzZXAgPSAiIikKYGBgCgoKI011bHRpdmFyaWF0ZSBjb3ggcmVncmVzc2lvbiBhdCBTdXJ2ZWlsbGFuY2UgV2luZG93IGZvciBSRlMKYGBge3J9CnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikgCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfQ2xpbmljYWwgRGF0YSAxMDIwMjUuY3N2IikKY2lyY19kYXRhIDwtIGNpcmNfZGF0YVtjaXJjX2RhdGEkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpjaXJjX2RhdGEgPC0gY2lyY19kYXRhW2NpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UhPSIiLF0KCmNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjdEROQS5TdXJ2ZWlsbGFuY2UsIGxldmVscz1jKCJORUdBVElWRSIsIlBPU0lUSVZFIiksIGxhYmVscyA9IGMoIk5lZ2F0aXZlIiwgIlBvc2l0aXZlIikpCmNpcmNfZGF0YSRjU3RhZ2UgPC0gZmFjdG9yKGNpcmNfZGF0YSRjU3RhZ2UsIGxldmVscyA9IGMoIjAvSSIsICJJSS9JSUkiKSkKY2lyY19kYXRhJE5BQyA8LSBmYWN0b3IoY2lyY19kYXRhJE5BQywgbGV2ZWxzID0gYygiRkFMU0UiLCAiVFJVRSIpKQpjaXJjX2RhdGEkQUNUIDwtIGZhY3RvcihjaXJjX2RhdGEkQUNULCBsZXZlbHMgPSBjKCJGQUxTRSIsICJUUlVFIikpCnN1cnZfb2JqZWN0IDwtIFN1cnYodGltZSA9IGNpcmNfZGF0YSRSRlMubW9udGhzLCBldmVudCA9IGNpcmNfZGF0YSRSRlMuRXZlbnQpIApjb3hfZml0IDwtIGNveHBoKHN1cnZfb2JqZWN0IH4gY3RETkEuU3VydmVpbGxhbmNlICsgQWdlICsgY1N0YWdlICsgTkFDICsgQUNULCBkYXRhPWNpcmNfZGF0YSkgCmdnZm9yZXN0KGNveF9maXQsIGRhdGEgPSBjaXJjX2RhdGEsIG1haW4gPSAiTXVsdGl2YXJpYXRlIFJlZ3Jlc3Npb24gTW9kZWwgZm9yIFJGUyAtIEFsbCBTdGFnZXMiLCByZWZMYWJlbCA9ICJSZWZlcmVuY2UgR3JvdXAiKQp0ZXN0LnBoIDwtIGNveC56cGgoY294X2ZpdCkKYGBgCgojY3RETkEgYW5kIE1UTS9tTCBEeW5hbWljcyBmb3IgcHRzIGF0IHN1cnZlaWxsYW5jZSB3aW5kb3cKYGBge3J9CiNEeW5hbWljcyBhbmQgTVRNL21MIHBsb3RzIGZvciBwYXRpZW50cyB3aXRoIGN0RE5BIG5lZ2F0aXZlIGF0IHN1cnZlaWxsYW5jZQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmRmIDwtIHJlYWQuY3N2KCJDTElBIFVUVUMgY3RETkEgTVRNLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKZGYgPC0gZGZbZGYkRmluYWwuY29ob3J0PT0iVFJVRSIsXQpkZiA8LSBkZltkZiRjdEROQS5TdXJ2ZWlsbGFuY2U9PSJORUdBVElWRSIsXQoKZGYkUkZTLkV2ZW50IDwtIGlmZWxzZShkZiRSRlMuRXZlbnQgJWluJSBjKCJObyIsICJubyIsICJGQUxTRSIsICJGYWxzZSIsICIwIiksIEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShkZiRSRlMuRXZlbnQgJWluJSBjKCJZZXMiLCAieWVzIiwgIlRSVUUiLCAiVHJ1ZSIsICIxIiksIFRSVUUsIE5BKSkKZGYkUkZTLkV2ZW50IDwtIGZhY3RvcihkZiRSRlMuRXZlbnQsIGxldmVscyA9IGMoRkFMU0UsIFRSVUUpKQpkZiA8LSBkZiAlPiUKICBncm91cF9ieShQYXRpZW50TmFtZSkgJT4lCiAgZmlsdGVyKG4oKSA+PSAyKSAlPiUgI2tlZXAgb25seSBwdHMgd2l0aCBhdCBsZWFzdCAyIHBvc3Qtc3VyZ2VyeSB0aW1lIHBvaW50cwogIHVuZ3JvdXAoKQoKbnVtX3VuaXF1ZSA8LSBsZW5ndGgodW5pcXVlKGRmJFBhdGllbnROYW1lKSkKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIHBhdGllbnRzOiIsIG51bV91bmlxdWUsICJcbiIpCgpkZl9wYXRpZW50X3BmcyA8LSBkZiAlPiUKICBncm91cF9ieShQYXRpZW50TmFtZSkgJT4lCiAgZHBseXI6OnN1bW1hcml6ZSgKICAgIFBGU19UcnVlID0gYW55KFJGUy5FdmVudCA9PSBUUlVFLCBuYS5ybSA9IFRSVUUpLAogICAgUEZTX0ZhbHNlID0gYWxsKFJGUy5FdmVudCA9PSBGQUxTRSwgbmEucm0gPSBUUlVFKQogICkKCm51bV90cnVlIDwtIHN1bShkZl9wYXRpZW50X3BmcyRQRlNfVHJ1ZSkKbnVtX2ZhbHNlIDwtIHN1bShkZl9wYXRpZW50X3BmcyRQRlNfRmFsc2UpCgpjYXQoIk51bWJlciBvZiB1bmlxdWUgcGF0aWVudHMgd2l0aCBFdmVudDoiLCBudW1fdHJ1ZSwgIlxuIikKY2F0KCJOdW1iZXIgb2YgdW5pcXVlIHBhdGllbnRzIHdpdGggTm8gRXZlbnQ6IiwgbnVtX2ZhbHNlLCAiXG4iKQoKcCA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gZGF0ZS5kaWZmLm1vbnRocywgCiAgICAgICAgICAgICAgICAgICAgeSA9IE1UTS5tTCwgCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBQYXRpZW50TmFtZSwgCiAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBSRlMuRXZlbnQpKSArCiAgZ2VvbV9saW5lKCkgKyAgICAgICMgQ29ubmVjdCB0aW1lcG9pbnRzIGZvciBlYWNoIHBhdGllbnQKICBnZW9tX3BvaW50KCkgKyAgICAgIyBBZGQgcG9pbnRzIGZvciBlYWNoIHRpbWVwb2ludAogICMgVXNlIGEgbG9nMTAgc2NhbGUgZm9yIHRoZSB5LWF4aXMgd2l0aCBzcGVjaWZpZWQgYnJlYWtzCiAgc2NhbGVfeV9sb2cxMChicmVha3MgPSBjKDAuMDEsIDAuMSwgMSwgMTAsIDEwMCwgMTAwMCksCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwLjAxIiwiMC4xIiwgIjEiLCAiMTAiLCAiMTAwIiwgIjEwMDAiKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgbWF4KGRmJGRhdGUuZGlmZi5tb250aHMsIG5hLnJtID0gVFJVRSksIGJ5ID0gNikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiRkFMU0UiID0gImJsdWUiLCAiVFJVRSIgPSAicmVkIikpICsKICBsYWJzKAogICAgeCA9ICJUaW1lIFNpbmNlIFN1cmdlcnkgb3Igc3RhcnQgb2YgZGVmaW5pdGl2ZSB0cmVhdG1lbnQgKG1vbnRocykiLAogICAgeSA9ICJNZWFuIFR1bW9yIE1vbGVjdWxlcyBwZXIgbUwgKE1UTS9tTCkiLAogICAgY29sb3IgPSAiUkZTIEV2ZW50IgogICkgKwogIHRoZW1lX21pbmltYWwoKQpwcmludChwKQoKI0R5bmFtaWNzIGFuZCBNVE0vbUwgcGxvdHMgZm9yIHBhdGllbnRzIHdpdGggY3RETkEgcG9zaXRpdmUgYXQgc3VydmVpbGxhbmNlCnJtKGxpc3Q9bHMoKSkKc2V0d2QoIn4vRG93bmxvYWRzIikKZGYgPC0gcmVhZC5jc3YoIkNMSUEgVVRVQyBjdEROQSBNVE0uY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpkZiA8LSBkZltkZiRGaW5hbC5jb2hvcnQ9PSJUUlVFIixdCmRmIDwtIGRmW2RmJGN0RE5BLlN1cnZlaWxsYW5jZT09IlBPU0lUSVZFIixdCgpkZiRSRlMuRXZlbnQgPC0gaWZlbHNlKGRmJFJGUy5FdmVudCAlaW4lIGMoIk5vIiwgIm5vIiwgIkZBTFNFIiwgIkZhbHNlIiwgIjAiKSwgRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGRmJFJGUy5FdmVudCAlaW4lIGMoIlllcyIsICJ5ZXMiLCAiVFJVRSIsICJUcnVlIiwgIjEiKSwgVFJVRSwgTkEpKQpkZiRSRlMuRXZlbnQgPC0gZmFjdG9yKGRmJFJGUy5FdmVudCwgbGV2ZWxzID0gYyhGQUxTRSwgVFJVRSkpCmRmIDwtIGRmICU+JQogIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICBmaWx0ZXIobigpID49IDIpICU+JSAja2VlcCBvbmx5IHB0cyB3aXRoIGF0IGxlYXN0IDIgcG9zdC1zdXJnZXJ5IHRpbWUgcG9pbnRzCiAgdW5ncm91cCgpCgpudW1fdW5pcXVlIDwtIGxlbmd0aCh1bmlxdWUoZGYkUGF0aWVudE5hbWUpKQpjYXQoIk51bWJlciBvZiB1bmlxdWUgcGF0aWVudHM6IiwgbnVtX3VuaXF1ZSwgIlxuIikKCmRmX3BhdGllbnRfcGZzIDwtIGRmICU+JQogIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKAogICAgUEZTX1RydWUgPSBhbnkoUkZTLkV2ZW50ID09IFRSVUUsIG5hLnJtID0gVFJVRSksCiAgICBQRlNfRmFsc2UgPSBhbGwoUkZTLkV2ZW50ID09IEZBTFNFLCBuYS5ybSA9IFRSVUUpCiAgKQoKbnVtX3RydWUgPC0gc3VtKGRmX3BhdGllbnRfcGZzJFBGU19UcnVlKQpudW1fZmFsc2UgPC0gc3VtKGRmX3BhdGllbnRfcGZzJFBGU19GYWxzZSkKCmNhdCgiTnVtYmVyIG9mIHVuaXF1ZSBwYXRpZW50cyB3aXRoIEV2ZW50OiIsIG51bV90cnVlLCAiXG4iKQpjYXQoIk51bWJlciBvZiB1bmlxdWUgcGF0aWVudHMgd2l0aCBObyBFdmVudDoiLCBudW1fZmFsc2UsICJcbiIpCgpwIDwtIGdncGxvdChkZiwgYWVzKHggPSBkYXRlLmRpZmYubW9udGhzLCAKICAgICAgICAgICAgICAgICAgICB5ID0gTVRNLm1MLCAKICAgICAgICAgICAgICAgICAgICBncm91cCA9IFBhdGllbnROYW1lLCAKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFJGUy5FdmVudCkpICsKICBnZW9tX2xpbmUoKSArICAgICAgIyBDb25uZWN0IHRpbWVwb2ludHMgZm9yIGVhY2ggcGF0aWVudAogIGdlb21fcG9pbnQoKSArICAgICAjIEFkZCBwb2ludHMgZm9yIGVhY2ggdGltZXBvaW50CiAgIyBVc2UgYSBsb2cxMCBzY2FsZSBmb3IgdGhlIHktYXhpcyB3aXRoIHNwZWNpZmllZCBicmVha3MKICBzY2FsZV95X2xvZzEwKGJyZWFrcyA9IGMoMC4wMSwgMC4xLCAxLCAxMCwgMTAwLCAxMDAwKSwKICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAuMDEiLCIwLjEiLCAiMSIsICIxMCIsICIxMDAiLCAiMTAwMCIpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCBtYXgoZGYkZGF0ZS5kaWZmLm1vbnRocywgbmEucm0gPSBUUlVFKSwgYnkgPSA2KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJGQUxTRSIgPSAiYmx1ZSIsICJUUlVFIiA9ICJyZWQiKSkgKwogIGxhYnMoCiAgICB4ID0gIlRpbWUgU2luY2UgU3VyZ2VyeSBvciBzdGFydCBvZiBkZWZpbml0aXZlIHRyZWF0bWVudCAobW9udGhzKSIsCiAgICB5ID0gIk1lYW4gVHVtb3IgTW9sZWN1bGVzIHBlciBtTCAoTVRNL21MKSIsCiAgICBjb2xvciA9ICJSRlMgRXZlbnQiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCnByaW50KHApCmBgYAoKI2N0RE5BIHZlbG9jaXR5IGFuZCBsZWFkIHRpbWUgbGluZXIgcmVncmVzc2lvbgpgYGB7cn0Kcm0obGlzdD1scygpKQpjc3ZfcGF0aCA8LSAifi9Eb3dubG9hZHMvQ0xJQSBVVFVDX2N0RE5BIHZlbG9jaXR5LmNzdiIgICMgPC0gYWRqdXN0IGlmIG5lZWRlZApkZl9yYXcgPC0gcmVhZC5jc3YoY3N2X3BhdGgsIGNoZWNrLm5hbWVzID0gRkFMU0UpCmRmIDwtIGRmX3JhdyAlPiUKICByZW5hbWUoTVRNX21MID0gYE1UTS5tTGApICU+JQogIG11dGF0ZSgKICAgIGRheXNDUi5tb250aHMgPSBhcy5udW1lcmljKGRheXNDUi5tb250aHMpLAogICAgTVRNX21MID0gYXMubnVtZXJpYyhNVE1fbUwpCiAgKSAlPiUKICBmaWx0ZXIoRmluYWwuY29ob3J0ID09IFRSVUUsICFpcy5uYShQYXRpZW50TmFtZSksICFpcy5uYShkYXlzQ1IubW9udGhzKSwgIWlzLm5hKE1UTV9tTCkpCgpwcmVDUiA8LSBkZiAlPiUKICBmaWx0ZXIoZGF5c0NSLm1vbnRocyA8PSAwLCBpcy5maW5pdGUoTVRNX21MKSwgTVRNX21MID4gMCkKCmVsaWdpYmxlIDwtIHByZUNSICU+JQogIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICBmaWx0ZXIobigpID49IDIsIG5fZGlzdGluY3QoZGF5c0NSLm1vbnRocykgPj0gMikgJT4lCiAgdW5ncm91cCgpCmlmIChucm93KGVsaWdpYmxlKSA9PSAwKSB7CiAgd2FybmluZygiTm8gcGF0aWVudHMgaGF2ZSDiiaUyIHZhbGlkIHByZS1yZWN1cnJlbmNlIHBvaW50cyB3aXRoIGRpc3RpbmN0IHRpbWVzOyByZWdyZXNzaW9uIGxpbmVzIHdpbGwgYmUgb21pdHRlZC4iKQp9CgpmaXRzIDwtIGVsaWdpYmxlICU+JQogIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICBzdW1tYXJpc2UoeF9taW4gPSBtaW4oZGF5c0NSLm1vbnRocywgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgbXV0YXRlKGdyaWQgPSBtYXAoeF9taW4sIH5zZXEoLngsIDAsIGxlbmd0aC5vdXQgPSA1MCkpKSAlPiUKICBzZWxlY3QoUGF0aWVudE5hbWUsIGdyaWQpCgpwcmVkaWN0X3BhdGllbnQgPC0gZnVuY3Rpb24oZGF0LCBuZXd4KSB7CiAgaWYgKGxlbmd0aChuZXd4KSA9PSAwKSB7CiAgICByZXR1cm4odGliYmxlKGRheXNDUi5tb250aHMgPSBudW1lcmljKDApLCBNVE1fbUwgPSBudW1lcmljKDApKSkKICB9CiAgZGF0MiA8LSBkYXQgJT4lCiAgICBmaWx0ZXIoaXMuZmluaXRlKE1UTV9tTCksIE1UTV9tTCA+IDApICU+JQogICAgbXV0YXRlKGxvZ19jdGRuYSA9IGxvZzEwKE1UTV9tTCkpCiAgaWYgKG5yb3coZGF0MikgPCAyIHx8IG5fZGlzdGluY3QoZGF0MiRkYXlzQ1IubW9udGhzKSA8IDIgfHwgYW55KCFpcy5maW5pdGUoZGF0MiRsb2dfY3RkbmEpKSkgewogICAgcmV0dXJuKHRpYmJsZShkYXlzQ1IubW9udGhzID0gbnVtZXJpYygwKSwgTVRNX21MID0gbnVtZXJpYygwKSkpCiAgfQogIG0gPC0gbG0obG9nX2N0ZG5hIH4gZGF5c0NSLm1vbnRocywgZGF0YSA9IGRhdDIpCiAgdGliYmxlKAogICAgZGF5c0NSLm1vbnRocyA9IG5ld3gsCiAgICBNVE1fbUwgPSAxMCBeIHByZWRpY3QobSwgbmV3ZGF0YSA9IHRpYmJsZShkYXlzQ1IubW9udGhzID0gbmV3eCkpCiAgKQp9CgpwcmVkX2xpbmVzIDwtIGVsaWdpYmxlICU+JQogIGdyb3VwX2J5KFBhdGllbnROYW1lKSAlPiUKICB0aWR5cjo6bmVzdCgpICU+JSAgICAgICAgICAgICAgICAgICAgICAgIyBsaXN0LWNvbHVtbiAiZGF0YSIgcGVyIHBhdGllbnQKICBsZWZ0X2pvaW4oZml0cywgYnkgPSAiUGF0aWVudE5hbWUiKSAlPiUgIyBsaXN0LWNvbHVtbiAiZ3JpZCIgcGVyIHBhdGllbnQKICBtdXRhdGUocHJlZCA9IG1hcDIoZGF0YSwgZ3JpZCwgfnByZWRpY3RfcGF0aWVudCgueCwgLnkpKSkgJT4lCiAgc2VsZWN0KFBhdGllbnROYW1lLCBwcmVkKSAlPiUKICB0aWR5cjo6dW5uZXN0KHByZWQpCgpwb29sZWRfbGluZSA8LSB7CiAgaWYgKG5yb3cocHJlQ1IpID49IDIgJiYgbl9kaXN0aW5jdChwcmVDUiRkYXlzQ1IubW9udGhzKSA+PSAyKSB7CiAgICBwb29sZWRfeCA8LSBzZXEobWluKHByZUNSJGRheXNDUi5tb250aHMsIG5hLnJtID0gVFJVRSksIDAsIGxlbmd0aC5vdXQgPSAxMDApCiAgICBwb29sZWRfZml0IDwtIGxtKGxvZzEwKE1UTV9tTCkgfiBkYXlzQ1IubW9udGhzLCBkYXRhID0gcHJlQ1IpCiAgICB0aWJibGUoCiAgICAgIGRheXNDUi5tb250aHMgPSBwb29sZWRfeCwKICAgICAgTVRNX21MID0gMTAgXiBwcmVkaWN0KHBvb2xlZF9maXQsIG5ld2RhdGEgPSB0aWJibGUoZGF5c0NSLm1vbnRocyA9IHBvb2xlZF94KSkKICAgICkKICB9IGVsc2UgewogICAgdGliYmxlKGRheXNDUi5tb250aHMgPSBudW1lcmljKDApLCBNVE1fbUwgPSBudW1lcmljKDApKQogIH0KfQoKeF9taW4gPC0gZmxvb3IobWluKGRmJGRheXNDUi5tb250aHMsIG5hLnJtID0gVFJVRSkgLyAzKSAqIDMKeF9tYXggPC0gY2VpbGluZyhtYXgoZGYkZGF5c0NSLm1vbnRocywgbmEucm0gPSBUUlVFKSAvIDMpICogMwp5X21pbl9wb3MgPC0gbWF4KG1pbihkZiRNVE1fbUxbZGYkTVRNX21MID4gMF0sIG5hLnJtID0gVFJVRSkgLyAyLCAwLjAxKQp5X21heF9wb3MgPC0gMTAgXiBjZWlsaW5nKGxvZzEwKG1heChkZiRNVE1fbUwsIG5hLnJtID0gVFJVRSkpKQpsb2dfYnJlYWtzIDwtIDEwIF4gc2VxKGZsb29yKGxvZzEwKHlfbWluX3BvcykpLCBjZWlsaW5nKGxvZzEwKHlfbWF4X3BvcykpKQoKcCA8LSBnZ3Bsb3QoKSArCiAgIyBwZXItcGF0aWVudCBmaXR0ZWQgbGluZXMgKGlmIGFueSkKICBnZW9tX2xpbmUoZGF0YSA9IHByZWRfbGluZXMsCiAgICAgICAgICAgIGFlcyh4ID0gZGF5c0NSLm1vbnRocywgeSA9IE1UTV9tTCwgY29sb3IgPSBQYXRpZW50TmFtZSksCiAgICAgICAgICAgIGxpbmV3aWR0aCA9IDEpICsKICAjIHBvb2xlZCBkYXNoZWQgdHJlbmQgKGlmIGFueSkKICBnZW9tX2xpbmUoZGF0YSA9IHBvb2xlZF9saW5lLAogICAgICAgICAgICBhZXMoeCA9IGRheXNDUi5tb250aHMsIHkgPSBNVE1fbUwpLAogICAgICAgICAgICBsaW5ld2lkdGggPSAwLjgsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXkzNSIpICsKICAjIHJhdyBwb2ludHMgKGFsbCB0aW1lcG9pbnRzLCBiZWZvcmUgYW5kIGFmdGVyKQogIGdlb21fcG9pbnQoZGF0YSA9IGRmLAogICAgICAgICAgICAgYWVzKHggPSBkYXlzQ1IubW9udGhzLCB5ID0gTVRNX21MLCBjb2xvciA9IFBhdGllbnROYW1lKSwKICAgICAgICAgICAgIHNpemUgPSAxLjgsIGFscGhhID0gMC44NSkgKwogICMgdmVydGljYWwgbGluZSBhdCBpbWFnaW5nLXBvc2l0aXZlIGRhdGUKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBsaW5ld2lkdGggPSAwLjgsIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX3lfbG9nMTAoYnJlYWtzID0gbG9nX2JyZWFrcywKICAgICAgICAgICAgICAgIGxhYmVscyA9IGxhYmVsX251bWJlcihhY2N1cmFjeSA9IDEpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSh4X21pbiwgeF9tYXgsIGJ5ID0gMykpICsgICMgZXZlcnkgMyBtb250aHMKICBsYWJzKAogICAgeCA9ICJNb250aHMgYmVmb3JlL2FmdGVyIGNsaW5pY2FsIHJlY3VycmVuY2UgKDAgPSBpbWFnaW5nIHBvc2l0aXZlKSIsCiAgICB5ID0gImN0RE5BIGxldmVsIChNVE0vbUwpIiwKICAgIGNvbG9yID0gIlBhdGllbnQiCiAgKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTIpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTg1IiwgbGluZXdpZHRoID0gMC4zKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTkyIiwgbGluZXdpZHRoID0gMC4yKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiAgIyBjaGFuZ2UgdG8gInJpZ2h0IiBpZiB5b3Ugd2FudCB0aGUgbGVnZW5kCiAgKQoKcHJpbnQocCkKYGBgCgoKI1RpbWUgZnJvbSBmaXJzdCBjdEROQSBwb3NpdGl2ZSB0aW1lcG9pbnQgdG8gbGFzdCBpbWFnaW5nIGluIHN1cnZlaWxsYW5jZV9GYWxzZSBQb3NpdGl2ZSBwYXRpZW50cwpgYGB7cn0Kcm0obGlzdD1scygpKQpjc3ZfcGF0aCA8LSAifi9Eb3dubG9hZHMvQ0xJQSBVVFVDX1RpbWUgZnJvbSBjdEROQSB0byBpbWFnaW5nIEZQIHB0cy5jc3YiCmRmIDwtIHJlYWQuY3N2KGNzdl9wYXRoLCBjaGVjay5uYW1lcyA9IEZBTFNFKQppZiAoIkZpbmFsLmNvaG9ydCIgJWluJSBuYW1lcyhkZikpIHsKICBkZiA8LSBkZltkZiRGaW5hbC5jb2hvcnQgPT0gVFJVRSwgXQp9IGVsc2UgewogIHdhcm5pbmcoIkNvbHVtbiAnRmluYWwuY29ob3J0JyBub3QgZm91bmQgaW4gdGhlIGRhdGFzZXQuIikKfQppZF9jYW5kaWRhdGVzIDwtIGMoIlBhdGllbnQiLCJJRCIsIlB0IiwiU3ViamVjdCIsIlNhbXBsZSIpCmlkX2NvbCA8LSBpZF9jYW5kaWRhdGVzW2lkX2NhbmRpZGF0ZXMgJWluJSBuYW1lcyhkZildWzFdCmlmIChpcy5uYShpZF9jb2wpKSB7CiAgZGYgPC0gZGYgJT4lIG11dGF0ZShQYXRpZW50ID0gcm93X251bWJlcigpKQogIGlkX2NvbCA8LSAiUGF0aWVudCIKfQoKbnVtX2NvbHMgPC0gaW50ZXJzZWN0KGMoIk1SIiwiQ1IiLCJMVCIpLCBuYW1lcyhkZikpCmRmW251bV9jb2xzXSA8LSBsYXBwbHkoZGZbbnVtX2NvbHNdLCBmdW5jdGlvbih4KSBzdXBwcmVzc1dhcm5pbmdzKGFzLm51bWVyaWMoeCkpKQoKIyBJZiBMVCBtaXNzaW5nLCBjb21wdXRlIGluIGRheXMKaWYgKCEoIkxUIiAlaW4lIG5hbWVzKGRmKSkpIHsKICBkZiA8LSBkZiAlPiUgbXV0YXRlKExUID0gQ1IgLSBNUikKfQoKIyAtLS0tIGNvbnZlcnQgdG8gbW9udGhzIC0tLS0KIyBBcHByb3hpbWF0ZTogMzAuNDQgZGF5cyBwZXIgbW9udGggKGF2ZXJhZ2UpCmRheXNfdG9fbW9udGhzIDwtIGZ1bmN0aW9uKHgpIHggLyAzMC40MzcKCmRmIDwtIGRmICU+JQogIG11dGF0ZShNUl9tbyA9IGRheXNfdG9fbW9udGhzKE1SKSwKICAgICAgICAgQ1JfbW8gPSBkYXlzX3RvX21vbnRocyhDUiksCiAgICAgICAgIExUX21vID0gZGF5c190b19tb250aHMoTFQpKQoKIyAtLS0tIG9yZGVyaW5nIGZvciB5LWF4aXMgLS0tLQpkZiA8LSBkZiAlPiUKICBtdXRhdGUoRWFybGllc3QgPSBwbWluKE1SX21vLCBDUl9tbywgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShFYXJsaWVzdCkgJT4lCiAgbXV0YXRlKFBhdGllbnRfZiA9IGZhY3RvciguZGF0YVtbaWRfY29sXV0sIGxldmVscyA9IC5kYXRhW1tpZF9jb2xdXSkpCgojIC0tLS0gbG9uZyBmb3JtYXQgZm9yIHBvaW50cyAtLS0tCnBvaW50c19sb25nIDwtIGRmICU+JQogIHNlbGVjdChQYXRpZW50X2YsIE1SX21vLCBDUl9tbykgJT4lCiAgcGl2b3RfbG9uZ2VyKGMoTVJfbW8sIENSX21vKSwgbmFtZXNfdG8gPSAiVHlwZSIsIHZhbHVlc190byA9ICJNb250aHMiKSAlPiUKICBtdXRhdGUoVHlwZSA9IGRwbHlyOjpyZWNvZGUoVHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1SX21vIiA9ICJGaXJzdCBjdEROQSBwb3NpdGl2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDUl9tbyIgPSAiTGFzdCBSYWRpb2xvZ2ljYWwgYXNzZXNzbWVudCIpKQoKIyAtLS0tIHNlZ21lbnRzIGJldHdlZW4gTVIgYW5kIENSIC0tLS0Kc2VnbWVudHNfZGYgPC0gZGYgJT4lCiAgdHJhbnNtdXRlKFBhdGllbnRfZiwKICAgICAgICAgICAgeDAgPSBNUl9tbywgeDEgPSBDUl9tbywKICAgICAgICAgICAgbHRfZmxhZyA9IGlmX2Vsc2UoTFQgPiAxMjAsICJndDEyMCIsICJsZTEyMCIpKQoKIyAtLS0tIGFubm90YXRpb24gLS0tLQptZWRfbHQgIDwtIG1lZGlhbihkZiRMVF9tbywgbmEucm0gPSBUUlVFKQptaW5fbHQgIDwtIG1pbihkZiRMVF9tbywgbmEucm0gPSBUUlVFKQptYXhfbHQgIDwtIG1heChkZiRMVF9tbywgbmEucm0gPSBUUlVFKQoKYW5ub3RfbGFiZWwgPC0gc3ByaW50ZigiTWVkaWFuIHRpbWUgZnJvbSBjdEROQSBwb3NpdGl2ZSB0byBsYXN0IGltYWdpbmc6XG4lLjFmIG1vbnRocyAoJS4xZiB0byAlLjFmKSIsCiAgICAgICAgICAgICAgICAgICAgICAgbWVkX2x0LCBtaW5fbHQsIG1heF9sdCkKCiMgLS0tLSBwbG90IC0tLS0KcGFsIDwtIGMoIkZpcnN0IGN0RE5BIHBvc2l0aXZlIiA9ICJibGFjayIsCiAgICAgICAgICJMYXN0IFJhZGlvbG9naWNhbCBhc3Nlc3NtZW50IiAgPSAicmVkIikKCnhfbWF4IDwtIG1heChjKGRmJE1SX21vLCBkZiRDUl9tbyksIG5hLnJtID0gVFJVRSkKeV9taWQgPC0gbGV2ZWxzKGRmJFBhdGllbnRfZilbY2VpbGluZyhubGV2ZWxzKGRmJFBhdGllbnRfZikgKiAwLjU1KV0KCnAgPC0gZ2dwbG90KCkgKwogIGdlb21fc2VnbWVudChkYXRhID0gc2VnbWVudHNfZGYsCiAgICAgICAgICAgICAgIGFlcyh4ID0geDAsIHhlbmQgPSB4MSwgeSA9IFBhdGllbnRfZiwgeWVuZCA9IFBhdGllbnRfZiwKICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gbHRfZmxhZyksCiAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDAuNiwgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMobGUxMjAgPSAic29saWQiLCBndDEyMCA9ICJkYXNoZWQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSAibm9uZSIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBwb2ludHNfbG9uZywKICAgICAgICAgICAgIGFlcyh4ID0gTW9udGhzLCB5ID0gUGF0aWVudF9mLCBjb2xvciA9IFR5cGUpLAogICAgICAgICAgICAgc2l6ZSA9IDIuOCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwsIG5hbWUgPSBOVUxMKSArCiAgbGFicyh4ID0gIk1vbnRocyBmcm9tIGN0RE5BIHBvc2l0aXZpdHkiLCB5ID0gTlVMTCkgKwogIGFubm90YXRlKCJ0ZXh0IiwKICAgICAgICAgICB4ID0geF9tYXggKiAwLjczLAogICAgICAgICAgIHkgPSB5X21pZCwKICAgICAgICAgICBsYWJlbCA9IGFubm90X2xhYmVsLAogICAgICAgICAgIGhqdXN0ID0gMCwgdmp1c3QgPSAwLjUsIHNpemUgPSAzLjgpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIGNlaWxpbmcoeF9tYXgvMykqMywgYnkgPSAzKSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCB4X21heCAqIDEuMDUpKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTIpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTg1IiwgbGluZXdpZHRoID0gMC4zKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleTkyIiwgbGluZXdpZHRoID0gMC4yKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KQogICkKCnByaW50KHApCgp3Y19kZiA8LSBkZiAlPiUgZHBseXI6OnNlbGVjdChNUiwgQ1IpICU+JSB0aWR5cjo6ZHJvcF9uYSgpCnBhaXJlZF9kaWZmIDwtIHdjX2RmJENSIC0gd2NfZGYkTVIKCndpbHggPC0gd2lsY294LnRlc3QoCiAgeCA9IHdjX2RmJENSLCB5ID0gd2NfZGYkTVIsCiAgcGFpcmVkID0gVFJVRSwKICBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiLAogIGNvbmYuaW50ID0gVFJVRSwgICAgICAjIGdpdmVzIENJIGZvciB0aGUgSG9kZ2Vz4oCTTGVobWFubiBlc3RpbWF0ZSBvZiB0aGUgbWVkaWFuIGRpZmZlcmVuY2UKICBleGFjdCA9IEZBTFNFICAgICAgICAgIyBzYWZlciBmb3IgdGllcy9sYXJnZSBOCikKCldfc3RhdCAgIDwtIHVubmFtZSh3aWx4JHN0YXRpc3RpYykgICAgICAgICAgIyBXaWxjb3hvbiBWCnBfdmFsdWUgIDwtIHdpbHgkcC52YWx1ZQpITF9lc3QgICA8LSB1bm5hbWUod2lseCRlc3RpbWF0ZSkgICAgICAgICAgICMgbWVkaWFuIG9mIChDUiAtIE1SKQpITF9jaSAgICA8LSB3aWx4JGNvbmYuaW50ICAgICAgICAgICAgICAgICAgICMgQ0kgZm9yIG1lZGlhbiBkaWZmZXJlbmNlCgojIFNpbXBsZSBwLXZhbHVlIGZvcm1hdHRlciBmb3IgYW5ub3RhdGlvbi9wdWJsaWNhdGlvbgpmbXRfcCA8LSBmdW5jdGlvbihwKSB7CiAgaWYgKGlzLm5hKHApKSByZXR1cm4oIk5BIikKICBpZiAocCA8IDAuMDAxKSAiPCAwLjAwMSIgZWxzZSBzcHJpbnRmKCI9ICUuM2YiLCByb3VuZChwLCAzKSkKfQpwX3RleHQgPC0gcGFzdGUwKCJQICIsIGZtdF9wKHBfdmFsdWUpKSAgIyBlLmcuLCAiUCA9IDAuMDAzIiBvciAiUCA8IDAuMDAxIgoKIyBPcHRpb25hbDogcHJpbnQgYSBjb21wYWN0IHN1bW1hcnkKY2F0KCJcblBhaXJlZCBXaWxjb3hvbiBzaWduZWQtcmFuayB0ZXN0IChDUiB2cyBNUilcbiIsCiAgICAiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIiwKICAgIHNwcmludGYoIk4gcGFpcnM6ICVkIiwgbnJvdyh3Y19kZikpLCAiXG4iLAogICAgc3ByaW50ZigiVyAoVik6ICVnIiwgV19zdGF0KSwgIlxuIiwKICAgIHNwcmludGYoIlAtdmFsdWU6ICVzIiwgaWZlbHNlKHBfdmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCBzcHJpbnRmKCIlLjZmIiwgcF92YWx1ZSkpKSwgIlxuIiwKICAgIHNwcmludGYoIkhvZGdlc+KAk0xlaG1hbm4gbWVkaWFuIGRpZmZlcmVuY2UgKENSIC0gTVIpOiAlLjFmIGRheXMiLCBITF9lc3QpLCAiXG4iLAogICAgc3ByaW50ZigiOTUlJSBDSTogWyUuMWYsICUuMWZdIGRheXMiLCBITF9jaVsxXSwgSExfY2lbMl0pLCAiXG4iLCBzZXAgPSAiIikKYGBgCgoKI0luZGl2aWR1YWwgUHQgUGxvdHNfRmFsc2UgUG9zaXRpdmUgcGF0aWVudHMKYGBge3J9CiNGaWx0ZXIgZm9yIFBhdGllbnQgVVRVQy0wMTAKcm0obGlzdD1scygpKQpzZXR3ZCgifi9Eb3dubG9hZHMiKQpjaXJjX2RhdGEgPC0gcmVhZC5jc3YoIlJXRSBVVFVDX1B0IFNwZWNpZmljIFBsb3QgZGF0YS5jc3YiKQpwbG90X2RhdGEgPC0gY2lyY19kYXRhICU+JSAKICBmaWx0ZXIoUGF0aWVudE5hbWUgPT0gIlVUVUMtMDEwIikgCgojIDIuIFByb2Nlc3MgbGF5ZXJzCiMgSGFuZGxlIGN0RE5BIGxvZy1zY2FsZSBwc2V1ZG8temVybwpkYXRhX1NpZ25hdGVyYSA8LSBwbG90X2RhdGEgJT4lIAogIGZpbHRlcihFdmVudF90eXBlICVpbiUgYygiY3RETkFfcG9zIiwgImN0RE5BX25lZyIpKSAlPiUKICBtdXRhdGUoTVRNX0xvZyA9IGlmZWxzZShNVE0ubUwgPT0gMCwgMC4wMDEsIE1UTS5tTCkpCgpkYXRhX0ltYWdpbmcgPC0gcGxvdF9kYXRhICU+JSAKICBmaWx0ZXIoRXZlbnRfdHlwZSA9PSAiSW1hZ2luZyIpCgojIFRyZWF0bWVudCBibG9ja3MKZGF0YV9UcmVhdG1lbnQgPC0gcGxvdF9kYXRhICU+JSAKICBmaWx0ZXIoIWlzLm5hKFR4X3R5cGUpICYgVHhfdHlwZSAhPSAiTkEiICYgIWlzLm5hKFR4X3N0YXJ0Lm1vbnRocykpICU+JQogIGRpc3RpbmN0KFR4X3R5cGUsIFR4X3N0YXJ0Lm1vbnRocywgVHhfZW5kLm1vbnRocykKCiMgMy4gQ3JlYXRlIHRoZSBQbG90CmdncGxvdCgpICsKICAjIFRyZWF0bWVudCBSZWN0YW5nbGVzCiAgZ2VvbV9yZWN0KGRhdGEgPSBkYXRhX1RyZWF0bWVudCwgCiAgICAgICAgICAgIGFlcyh4bWluID0gVHhfc3RhcnQubW9udGhzLCB4bWF4ID0gVHhfZW5kLm1vbnRocywgeW1pbiA9IDAuMDA0LCB5bWF4ID0gMTApLCAKICAgICAgICAgICAgZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuMTUpICsKICBnZW9tX3RleHQoZGF0YSA9IGRhdGFfVHJlYXRtZW50LCAKICAgICAgICAgICAgYWVzKHggPSAoVHhfc3RhcnQubW9udGhzICsgVHhfZW5kLm1vbnRocykvMiwgeSA9IDUsIGxhYmVsID0gVHhfdHlwZSksIAogICAgICAgICAgICBhbmdsZSA9IDkwLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzLCBmb250ZmFjZSA9ICJib2xkIikgKwogIAogICMgY3RETkEgTGluZQogIGdlb21fbGluZShkYXRhID0gZGF0YV9TaWduYXRlcmEsIAogICAgICAgICAgICBhZXMoeCA9IGRhdGUuZGlmZi5tb250aHMsIHkgPSBNVE1fTG9nKSwgY29sb3IgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjcpICsKICAKICAjIGN0RE5BIFBvaW50cyAoTWFwcGVkIHRvIEZpbGwpCiAgZ2VvbV9wb2ludChkYXRhID0gZGF0YV9TaWduYXRlcmEsIAogICAgICAgICAgICAgYWVzKHggPSBkYXRlLmRpZmYubW9udGhzLCB5ID0gTVRNX0xvZywgZmlsbCA9IEV2ZW50X3R5cGUpLCAKICAgICAgICAgICAgIHNoYXBlID0gMjEsIHNpemUgPSAzLjUsIGNvbG9yID0gImJsYWNrIikgKwogIAogICMgSW1hZ2luZyBQb2ludHMgKE1hcHBlZCB0byBTaGFwZSBhbmQgQ29sb3IgdG8gY3JlYXRlIGEgc2Vjb25kIGxlZ2VuZCkKICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhX0ltYWdpbmcsIAogICAgICAgICAgICAgYWVzKHggPSBkYXRlLmRpZmYubW9udGhzLCB5ID0gMC4wMDUsIGNvbG9yID0gIk5FRCBpbiBTY2FuIiksIAogICAgICAgICAgICAgc2hhcGUgPSAyNSwgc2l6ZSA9IDQsIGZpbGwgPSAiZGFya2dyZWVuIikgKwogIAogICMgWC1BeGlzOiAzLW1vbnRoIGludGVydmFscwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgbWF4KHBsb3RfZGF0YSRkYXRlLmRpZmYubW9udGhzLCBuYS5ybT1UKSArIDMsIGJ5ID0gMykpICsKICAKICAjIFktQXhpczogTG9nMTAgc2NhbGUKICBzY2FsZV95X2xvZzEwKGJyZWFrcyA9IGMoMC4wMDEsIDAuMDEsIDAuMSwgMSwgMTApLAogICAgICAgICAgICAgICAgbGFiZWxzID0gYygiMCIsICIwLjAxIiwgIjAuMSIsICIxIiwgIjEwIiksCiAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAuMDAxLCAxNSkpICsKICAKICAjIExlZ2VuZCBmb3IgY3RETkEKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJjdEROQV9wb3MiID0gImJsYWNrIiwgImN0RE5BX25lZyIgPSAid2hpdGUiKSwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTmVnYXRpdmUiLCAiUG9zaXRpdmUiKSwgCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJjdEROQSBTdGF0dXMiKSArCiAgCiAgIyBMZWdlbmQgZm9yIEltYWdpbmcKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTkVEIGluIFNjYW4iID0gImRhcmtncmVlbiIpLCAKICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJJbWFnaW5nIFJlc3VsdCIpICsKICAKICAjIFN0eWxpbmcgdGhlIGxlZ2VuZCB0byBzaG93IHRoZSB0cmlhbmdsZSBjb3JyZWN0bHkKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaGFwZSA9IDI1LCBmaWxsID0gImRhcmtncmVlbiIpKSkgKwogIAogIGxhYnMoeCA9ICJNb250aHMgZnJvbSBTdXJnZXJ5IiwgCiAgICAgICB5ID0gIk1UTS9tTCIsIAogICAgICAgdGl0bGUgPSAiQ2xpbmljYWwgVGltZWxpbmU6IFVUVUMtMDEwIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQoKI0ZpbHRlciBmb3IgUGF0aWVudCBVVFVDLTAxMQpybShsaXN0PWxzKCkpCnNldHdkKCJ+L0Rvd25sb2FkcyIpCmNpcmNfZGF0YSA8LSByZWFkLmNzdigiUldFIFVUVUNfUHQgU3BlY2lmaWMgUGxvdCBkYXRhLmNzdiIpCnBsb3RfZGF0YSA8LSBjaXJjX2RhdGEgJT4lIAogIGZpbHRlcihQYXRpZW50TmFtZSA9PSAiVVRVQy0wMTEiKSAKCiMgMi4gUHJvY2VzcyBsYXllcnMKIyBIYW5kbGUgY3RETkEgbG9nLXNjYWxlIHBzZXVkby16ZXJvCmRhdGFfU2lnbmF0ZXJhIDwtIHBsb3RfZGF0YSAlPiUgCiAgZmlsdGVyKEV2ZW50X3R5cGUgJWluJSBjKCJjdEROQV9wb3MiLCAiY3RETkFfbmVnIikpICU+JQogIG11dGF0ZShNVE1fTG9nID0gaWZlbHNlKE1UTS5tTCA9PSAwLCAwLjAwMSwgTVRNLm1MKSkKCmRhdGFfSW1hZ2luZyA8LSBwbG90X2RhdGEgJT4lIAogIGZpbHRlcihFdmVudF90eXBlID09ICJJbWFnaW5nIikKCiMgVHJlYXRtZW50IGJsb2NrcwpkYXRhX1RyZWF0bWVudCA8LSBwbG90X2RhdGEgJT4lIAogIGZpbHRlcighaXMubmEoVHhfdHlwZSkgJiBUeF90eXBlICE9ICJOQSIgJiAhaXMubmEoVHhfc3RhcnQubW9udGhzKSkgJT4lCiAgZGlzdGluY3QoVHhfdHlwZSwgVHhfc3RhcnQubW9udGhzLCBUeF9lbmQubW9udGhzKQoKIyAzLiBDcmVhdGUgdGhlIFBsb3QKZ2dwbG90KCkgKwogICMgVHJlYXRtZW50IFJlY3RhbmdsZXMKICBnZW9tX3JlY3QoZGF0YSA9IGRhdGFfVHJlYXRtZW50LCAKICAgICAgICAgICAgYWVzKHhtaW4gPSBUeF9zdGFydC5tb250aHMsIHhtYXggPSBUeF9lbmQubW9udGhzLCB5bWluID0gMC4wMDQsIHltYXggPSAxMCksIAogICAgICAgICAgICBmaWxsID0gImxpZ2h0Ymx1ZSIsIGFscGhhID0gMC4xNSkgKwogIGdlb21fdGV4dChkYXRhID0gZGF0YV9UcmVhdG1lbnQsIAogICAgICAgICAgICBhZXMoeCA9IChUeF9zdGFydC5tb250aHMgKyBUeF9lbmQubW9udGhzKS8yLCB5ID0gNSwgbGFiZWwgPSBUeF90eXBlKSwgCiAgICAgICAgICAgIGFuZ2xlID0gOTAsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgCiAgIyBjdEROQSBMaW5lCiAgZ2VvbV9saW5lKGRhdGEgPSBkYXRhX1NpZ25hdGVyYSwgCiAgICAgICAgICAgIGFlcyh4ID0gZGF0ZS5kaWZmLm1vbnRocywgeSA9IE1UTV9Mb2cpLCBjb2xvciA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDAuNykgKwogIAogICMgY3RETkEgUG9pbnRzIChNYXBwZWQgdG8gRmlsbCkKICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhX1NpZ25hdGVyYSwgCiAgICAgICAgICAgICBhZXMoeCA9IGRhdGUuZGlmZi5tb250aHMsIHkgPSBNVE1fTG9nLCBmaWxsID0gRXZlbnRfdHlwZSksIAogICAgICAgICAgICAgc2hhcGUgPSAyMSwgc2l6ZSA9IDMuNSwgY29sb3IgPSAiYmxhY2siKSArCiAgCiAgIyBJbWFnaW5nIFBvaW50cyAoTWFwcGVkIHRvIFNoYXBlIGFuZCBDb2xvciB0byBjcmVhdGUgYSBzZWNvbmQgbGVnZW5kKQogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGFfSW1hZ2luZywgCiAgICAgICAgICAgICBhZXMoeCA9IGRhdGUuZGlmZi5tb250aHMsIHkgPSAwLjAwNSwgY29sb3IgPSAiTkVEIGluIFNjYW4iKSwgCiAgICAgICAgICAgICBzaGFwZSA9IDI1LCBzaXplID0gNCwgZmlsbCA9ICJkYXJrZ3JlZW4iKSArCiAgCiAgIyBYLUF4aXM6IDMtbW9udGggaW50ZXJ2YWxzCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCBtYXgocGxvdF9kYXRhJGRhdGUuZGlmZi5tb250aHMsIG5hLnJtPVQpICsgMywgYnkgPSAzKSkgKwogIAogICMgWS1BeGlzOiBMb2cxMCBzY2FsZQogIHNjYWxlX3lfbG9nMTAoYnJlYWtzID0gYygwLjAwMSwgMC4wMSwgMC4xLCAxLCAxMCksCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIwIiwgIjAuMDEiLCAiMC4xIiwgIjEiLCAiMTAiKSwKICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMC4wMDEsIDE1KSkgKwogIAogICMgTGVnZW5kIGZvciBjdEROQQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImN0RE5BX3BvcyIgPSAiYmxhY2siLCAiY3RETkFfbmVnIiA9ICJ3aGl0ZSIpLCAKICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJOZWdhdGl2ZSIsICJQb3NpdGl2ZSIpLCAKICAgICAgICAgICAgICAgICAgICBuYW1lID0gImN0RE5BIFN0YXR1cyIpICsKICAKICAjIExlZ2VuZCBmb3IgSW1hZ2luZwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJORUQgaW4gU2NhbiIgPSAiZGFya2dyZWVuIiksIAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkltYWdpbmcgUmVzdWx0IikgKwogIAogICMgU3R5bGluZyB0aGUgbGVnZW5kIHRvIHNob3cgdGhlIHRyaWFuZ2xlIGNvcnJlY3RseQogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNoYXBlID0gMjUsIGZpbGwgPSAiZGFya2dyZWVuIikpKSArCiAgCiAgbGFicyh4ID0gIk1vbnRocyBmcm9tIFN1cmdlcnkiLCAKICAgICAgIHkgPSAiTVRNL21MIiwgCiAgICAgICB0aXRsZSA9ICJDbGluaWNhbCBUaW1lbGluZTogVVRVQy0wMTEiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoK